X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=af975956dbca755091ca60981e5432e37b8328a4;hb=56d4fbf92eb6d8eb31c910e137224308c40dd909;hp=8caac7b597fb0f3393c77e1ad2fce3fabdb91bb3;hpb=825c6fe5eb362437aa46faa52b683a62eede9a13;p=elogind.git diff --git a/src/shared/util.c b/src/shared/util.c index 8caac7b59..af975956d 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -909,8 +909,7 @@ int load_env_file( continue; if (!(u = normalize_env_assignment(p))) { - log_error("Out of memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -918,8 +917,7 @@ int load_env_file( free(u); if (!t) { - log_error("Out of memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -1083,7 +1081,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * if (h < 0) return h; - r = join("[", t, "]", NULL); + r = strjoin("[", t, "]", NULL); free(t); if (!r) @@ -2385,8 +2383,9 @@ int acquire_terminal( bool ignore_tiocstty_eperm, usec_t timeout) { - int fd = -1, notify = -1, r, wd = -1; + int fd = -1, notify = -1, r = 0, wd = -1; usec_t ts = 0; + struct sigaction sa_old, sa_new; assert(name); @@ -2434,17 +2433,26 @@ int acquire_terminal( if (fd < 0) return fd; + /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed + * if we already own the tty. */ + zero(sa_new); + sa_new.sa_handler = SIG_IGN; + sa_new.sa_flags = SA_RESTART; + assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); + /* First, try to get the tty */ - r = ioctl(fd, TIOCSCTTY, force); + if (ioctl(fd, TIOCSCTTY, force) < 0) + r = -errno; + + assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); /* Sometimes it makes sense to ignore TIOCSCTTY * returning EPERM, i.e. when very likely we already * are have this controlling terminal. */ - if (r < 0 && errno == EPERM && ignore_tiocstty_eperm) + if (r < 0 && r == -EPERM && ignore_tiocstty_eperm) r = 0; - if (r < 0 && (force || fail || errno != EPERM)) { - r = -errno; + if (r < 0 && (force || fail || r != -EPERM)) { goto fail; } @@ -2940,7 +2948,8 @@ int make_stdio(int fd) { int make_null_stdio(void) { int null_fd; - if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0) + null_fd = open("/dev/null", O_RDWR|O_NOCTTY); + if (null_fd < 0) return -errno; return make_stdio(null_fd); @@ -3067,26 +3076,22 @@ bool hostname_is_set(void) { return !isempty(u.nodename) && !streq(u.nodename, "(none)"); } -char* getlogname_malloc(void) { - uid_t uid; + +static char *lookup_uid(uid_t uid) { long bufsize; char *buf, *name; struct passwd pwbuf, *pw = NULL; - struct stat st; - - if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0) - uid = st.st_uid; - else - uid = getuid(); /* Shortcut things to avoid NSS lookups */ if (uid == 0) return strdup("root"); - if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0) + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize <= 0) bufsize = 4096; - if (!(buf = malloc(bufsize))) + buf = malloc(bufsize); + if (!buf) return NULL; if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) { @@ -3103,6 +3108,28 @@ char* getlogname_malloc(void) { return name; } +char* getlogname_malloc(void) { + uid_t uid; + struct stat st; + + if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0) + uid = st.st_uid; + else + uid = getuid(); + + return lookup_uid(uid); +} + +char *getusername_malloc(void) { + const char *e; + + e = getenv("USER"); + if (e) + return strdup(e); + + return lookup_uid(getuid()); +} + int getttyname_malloc(int fd, char **r) { char path[PATH_MAX], *c; int k; @@ -3243,7 +3270,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { +int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { DIR *d; int ret = 0; @@ -3335,14 +3362,43 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root return ret; } -int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { - int fd; - int r; +int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { + struct statfs s; + + assert(fd >= 0); + + if (fstatfs(fd, &s) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + /* We refuse to clean disk file systems with this call. This + * is extra paranoia just to be sure we never ever remove + * non-state data */ + + if (s.f_type != TMPFS_MAGIC && + s.f_type != RAMFS_MAGIC) { + log_error("Attempted to remove disk file system, and we can't allow that."); + close_nointr_nofail(fd); + return -EPERM; + } + + return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev); +} + +static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) { + int fd, r; + struct statfs s; assert(path); - /* Be paranoid */ - assert(!streq(path, "/")); + /* We refuse to clean the root file system with this + * call. This is extra paranoia to never cause a really + * seriously broken system. */ + if (path_equal(path, "/")) { + log_error("Attempted to remove entire root file system, and we can't allow that."); + return -EPERM; + } fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (fd < 0) { @@ -3350,6 +3406,17 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky if (errno != ENOTDIR) return -errno; + if (!dangerous) { + if (statfs(path, &s) < 0) + return -errno; + + if (s.f_type != TMPFS_MAGIC && + s.f_type != RAMFS_MAGIC) { + log_error("Attempted to remove disk file system, and we can't allow that."); + return -EPERM; + } + } + if (delete_root && !only_dirs) if (unlink(path) < 0 && errno != ENOENT) return -errno; @@ -3357,8 +3424,21 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky return 0; } - r = rm_rf_children(fd, only_dirs, honour_sticky, NULL); + if (!dangerous) { + if (fstatfs(fd, &s) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (s.f_type != TMPFS_MAGIC && + s.f_type != RAMFS_MAGIC) { + log_error("Attempted to remove disk file system, and we can't allow that."); + close_nointr_nofail(fd); + return -EPERM; + } + } + r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL); if (delete_root) { if (honour_sticky && file_is_priv_sticky(path) > 0) @@ -3373,6 +3453,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky return r; } +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { + return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false); +} + +int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { + return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true); +} + int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); @@ -3686,18 +3774,27 @@ int fd_columns(int fd) { return ws.ws_col; } -unsigned columns(void) { - static __thread int parsed_columns = 0; +static unsigned columns_cached(bool cached) { + static __thread int parsed_columns = 0, env_columns = -1; const char *e; - if (_likely_(parsed_columns > 0)) + if (_likely_(parsed_columns > 0 && cached)) return parsed_columns; - e = getenv("COLUMNS"); - if (e) - parsed_columns = atoi(e); + if (_unlikely_(env_columns == -1)) { + e = getenv("COLUMNS"); + if (e) + env_columns = atoi(e); + else + env_columns = 0; + } - if (parsed_columns <= 0) + if (env_columns > 0) { + parsed_columns = env_columns; + return parsed_columns; + } + + if (parsed_columns <= 0 || !cached) parsed_columns = fd_columns(STDOUT_FILENO); if (parsed_columns <= 0) @@ -3706,6 +3803,14 @@ unsigned columns(void) { return parsed_columns; } +unsigned columns(void) { + return columns_cached(true); +} + +unsigned columns_uncached(void) { + return columns_cached(false); +} + int fd_lines(int fd) { struct winsize ws; zero(ws); @@ -4188,7 +4293,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) { continue; if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) { - log_error("Out of memory"); + log_oom(); continue; } @@ -4815,7 +4920,12 @@ int socket_from_display(const char *display, char **path) { return 0; } -int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) { +int get_user_creds( + const char **username, + uid_t *uid, gid_t *gid, + const char **home, + const char **shell) { + struct passwd *p; uid_t u; @@ -4836,6 +4946,10 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h if (home) *home = "/root"; + + if (shell) + *shell = "/bin/sh"; + return 0; } @@ -4867,6 +4981,9 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h if (home) *home = p->pw_dir; + if (shell) + *shell = p->pw_shell; + return 0; } @@ -5083,7 +5200,7 @@ finish: return r; } -char *join(const char *x, ...) { +char *strjoin(const char *x, ...) { va_list ap; size_t l; char *r, *p; @@ -5783,3 +5900,117 @@ void warn_melody(void) { ioctl(fd, KIOCSOUND, 0); close_nointr_nofail(fd); } + +int make_console_stdio(void) { + int fd, r; + + /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ + + fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1); + if (fd < 0) { + log_error("Failed to acquire terminal: %s", strerror(-fd)); + return fd; + } + + r = make_stdio(fd); + if (r < 0) { + log_error("Failed to duplicate terminal fd: %s", strerror(-r)); + return r; + } + + return 0; +} + +int get_home_dir(char **_h) { + char *h; + const char *e; + uid_t u; + struct passwd *p; + + assert(_h); + + /* Take the user specified one */ + e = getenv("HOME"); + if (e) { + h = strdup(e); + if (!h) + return -ENOMEM; + + *_h = h; + return 0; + } + + /* Hardcode home directory for root to avoid NSS */ + u = getuid(); + if (u == 0) { + h = strdup("/root"); + if (!h) + return -ENOMEM; + + *_h = h; + return 0; + } + + /* Check the database... */ + errno = 0; + p = getpwuid(u); + if (!p) + return errno ? -errno : -ENOENT; + + if (!path_is_absolute(p->pw_dir)) + return -EINVAL; + + h = strdup(p->pw_dir); + if (!h) + return -ENOMEM; + + *_h = h; + return 0; +} + +int get_shell(char **_sh) { + char *sh; + const char *e; + uid_t u; + struct passwd *p; + + assert(_sh); + + /* Take the user specified one */ + e = getenv("SHELL"); + if (e) { + sh = strdup(e); + if (!sh) + return -ENOMEM; + + *_sh = sh; + return 0; + } + + /* Hardcode home directory for root to avoid NSS */ + u = getuid(); + if (u == 0) { + sh = strdup("/bin/sh"); + if (!sh) + return -ENOMEM; + + *_sh = sh; + return 0; + } + + /* Check the database... */ + errno = 0; + p = getpwuid(u); + if (!p) + return errno ? -errno : -ESRCH; + + if (!path_is_absolute(p->pw_shell)) + return -EINVAL; + + sh = strdup(p->pw_shell); + if (!sh) + return -ENOMEM; + + *_sh = sh; + return 0; +}