-
Notifications
You must be signed in to change notification settings - Fork 124
Fix macOS filesystem security: use /etc/paths.d/TinyTeX when /usr/local/bin is not writable
#489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3f892a5
2f27f9e
28de5a7
31aaa1f
9d17b6e
0a2a348
eea0101
76970e1
14a151f
67441ce
cbeb770
6f22065
42fbe7f
71d49e3
598df3e
864bb51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| YEAR: 2017-2024 | ||
| YEAR: 2017-2026 | ||
| COPYRIGHT HOLDER: Yihui Xie and Posit Software, PBC |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,8 +33,8 @@ | |
| #' installed. By default, a vector of all currently installed LaTeX packages | ||
| #' if an existing installation of TinyTeX is found. If you want a fresh | ||
| #' installation, you may use \code{extra_packages = NULL}. | ||
| #' @param add_path Whether to run the command \command{tlmgr path add} to add | ||
| #' the bin path of TeX Live to the system environment variable \var{PATH}. | ||
| #' @param add_path Whether to add the bin path of TeX Live to the system | ||
| #' environment variable \var{PATH}. See \code{\link{tlmgr_path}()}. | ||
| #' @references See the TinyTeX documentation (\url{https://yihui.org/tinytex/}) | ||
| #' for the default installation directories on different platforms. | ||
| #' @note If you really want to disable the installation, you may set the | ||
|
|
@@ -113,7 +113,6 @@ install_tinytex = function( | |
| switch( | ||
| os, | ||
| 'unix' = { | ||
| check_local_bin() | ||
| if (os_index != 3 && !any(dir_exists(c('~/bin', '~/.local/bin')))) on.exit(message( | ||
| 'You may have to restart your system after installing TinyTeX to make sure ', | ||
| '~/bin appears in your PATH variable (https://github.com/rstudio/tinytex/issues/16).' | ||
|
|
@@ -290,25 +289,43 @@ win_app_dir = function(s) { | |
| # test if path is pure ASCII and has no spaces | ||
| valid_path = function(x) grepl('^[!-~]+$', x) | ||
|
|
||
| # check if /usr/local/bin on macOS is writable | ||
| check_local_bin = function() { | ||
| if (os_index != 3 || is_writable(p <- '/usr/local/bin')) return() | ||
| message( | ||
| 'The directory ', p, ' is not writable. I recommend that you make it writable. ', | ||
| 'See https://github.com/rstudio/tinytex/issues/24 for more info.' | ||
| osascript = function(cmd) { | ||
| message("Requesting admin privilege to run: sudo ", cmd) | ||
| escaped = gsub('"', '\\"', cmd, fixed = TRUE) | ||
| ret = system(sprintf( | ||
| "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", escaped | ||
| )) | ||
| if (ret != 0) warning( | ||
| "Please run the above command in your Terminal (password required).", call. = FALSE | ||
| ) | ||
| if (!dir_exists(p)) osascript(paste('mkdir -p', p)) | ||
| user = system2('whoami', stdout = TRUE) | ||
| osascript(sprintf('chown -R %s:admin %s', user, p)) | ||
| ret | ||
| } | ||
|
|
||
| osascript = function(cmd) { | ||
| if (system(sprintf( | ||
| "/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'", cmd | ||
| )) != 0) warning( | ||
| "Please run this command in your Terminal (password required):\n sudo ", | ||
| cmd, call. = FALSE | ||
| ) | ||
| # on macOS, if the user doesn't have write permission to /usr/local/bin, we use | ||
| # /etc/paths.d instead | ||
| use_paths_d = function() { | ||
| is_macos() && file.access('/usr/local/bin', 2) != 0 | ||
| } | ||
|
|
||
| # add/remove TinyTeX's bin path to/from /etc/paths.d/TinyTeX on macOS; | ||
| # if adding and the file already contains the desired path, skip the operation | ||
| macos_path = function(dir = NULL, action = 'add') { | ||
| paths_file = '/etc/paths.d/TinyTeX' | ||
| add = action == 'add' | ||
| cmd = if (add) { | ||
| if (is.null(dir) || dir == '') return(1L) | ||
| if (file.exists(paths_file) && | ||
| identical(readLines(paths_file, warn = FALSE), dir)) | ||
| return(0L) | ||
| tmp = tempfile() | ||
| writeLines(dir, tmp) | ||
| sprintf('cp "%s" "%s"', tmp, paths_file) | ||
| } else { | ||
| sprintf('rm -f "%s"', paths_file) | ||
| } | ||
| ret = osascript(cmd) | ||
| if (add && ret == 0) unlink(tmp) | ||
| ret | ||
| } | ||
|
|
||
| install_tinytex_source = function(repo = '', dir, version, add_path, extra_packages) { | ||
|
|
@@ -587,9 +604,9 @@ download_installer = function(file, version) { | |
| #' | ||
| #' The function \code{copy_tinytex()} copies the existing TinyTeX installation | ||
| #' to another directory (e.g., a portable device like a USB stick). The function | ||
| #' \code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX | ||
| #' in an existing folder to the \code{PATH} variable of the current system, so | ||
| #' that you can use utilities such as \command{tlmgr} and \command{pdflatex}, | ||
| #' \code{use_tinytex()} adds the copy of TinyTeX in an existing folder to the | ||
| #' \code{PATH} variable of the current system via \code{\link{tlmgr_path}()}, | ||
| #' so that you can use utilities such as \command{tlmgr} and \command{pdflatex}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't mention OS details; link to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 76970e1. |
||
| #' etc. | ||
| #' @param from The root directory of the TinyTeX installation. For | ||
| #' \code{copy_tinytex()}, the default value \code{tinytex_root()} should be a | ||
|
|
@@ -632,7 +649,8 @@ use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { | |
| if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") | ||
| p = file.path(d, 'tlmgr') | ||
| if (os == 'windows') p = paste0(p, '.bat') | ||
| if (system2(p, c('path', 'add')) != 0) stop( | ||
| ret = if (use_paths_d()) macos_path(normalizePath(d)) else system2(p, c('path', 'add')) | ||
| if (ret != 0) warning( | ||
| "Failed to add '", d, "' to your system's environment variable PATH. You may ", | ||
| "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." | ||
| ) | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before running system() command, emit a message() telling users the command to be executed so they know the purpose of requiring the password; after running system(), if it fails, add warning() to tell you to run the command in terminal (don't repeat the command in the warning message since it has been mentioned in message() before)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 71d49e3.
osascript()now emitsmessage("Requesting admin privilege to run: sudo ", cmd)before thesystem()call, and on failure emitswarning("Please run the above command in your Terminal (password required).")without repeating the command.