X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=os-posix.c;h=3ba7df8d7537512ab9157135e012ed1c7cdcb6a6;hb=d65af288a84d8bf8c27e55d45545f52f016c08a7;hp=61873014816e5b3471516092214b7b03df7f2e39;hpb=9fbee91a131a05e443d7108d7fbdf3ca91020290;p=mirror_qemu.git diff --git a/os-posix.c b/os-posix.c index 6187301481..3ba7df8d75 100644 --- a/os-posix.c +++ b/os-posix.c @@ -23,35 +23,36 @@ * THE SOFTWARE. */ -#include -#include -#include -#include +#include "qemu/osdep.h" #include -/*needed for MAP_POPULATE before including qemu-options.h */ -#include #include #include #include +#include "qemu-common.h" /* Needed early for CONFIG_BSD etc. */ -#include "config-host.h" #include "sysemu/sysemu.h" #include "net/slirp.h" #include "qemu-options.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/cutils.h" #ifdef CONFIG_LINUX #include #endif -#ifdef __FreeBSD__ -#include -#endif +/* + * Must set all three of these at once. + * Legal combinations are unset by name by uid + */ +static struct passwd *user_pwd; /* NULL non-NULL NULL */ +static uid_t user_uid = (uid_t)-1; /* -1 -1 >=0 */ +static gid_t user_gid = (gid_t)-1; /* -1 -1 >=0 */ -static struct passwd *user_pwd; static const char *chroot_dir; static int daemonize; -static int fds[2]; +static int daemon_pipe; void os_setup_early_signal_handling(void) { @@ -94,7 +95,7 @@ char *os_find_datadir(void) if (exec_dir == NULL) { return NULL; } - dir = dirname(exec_dir); + dir = g_path_get_dirname(exec_dir); max_len = strlen(dir) + MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; @@ -108,6 +109,7 @@ char *os_find_datadir(void) } } + g_free(dir); g_free(exec_dir); return res; } @@ -124,32 +126,58 @@ void os_set_proc_name(const char *s) /* Could rewrite argv[0] too, but that's a bit more complicated. This simple way is enough for `top'. */ if (prctl(PR_SET_NAME, name)) { - perror("unable to change process name"); + error_report("unable to change process name: %s", strerror(errno)); exit(1); } #else - fprintf(stderr, "Change of process name not supported by your OS\n"); + error_report("Change of process name not supported by your OS"); exit(1); #endif } + +static bool os_parse_runas_uid_gid(const char *optarg) +{ + unsigned long lv; + const char *ep; + uid_t got_uid; + gid_t got_gid; + int rc; + + rc = qemu_strtoul(optarg, &ep, 0, &lv); + got_uid = lv; /* overflow here is ID in C99 */ + if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) { + return false; + } + + rc = qemu_strtoul(ep + 1, 0, 0, &lv); + got_gid = lv; /* overflow here is ID in C99 */ + if (rc || got_gid != lv || got_gid == (gid_t)-1) { + return false; + } + + user_pwd = NULL; + user_uid = got_uid; + user_gid = got_gid; + return true; +} + /* * Parse OS specific command line options. * return 0 if option handled, -1 otherwise */ -void os_parse_cmd_args(int index, const char *optarg) +int os_parse_cmd_args(int index, const char *optarg) { switch (index) { -#ifdef CONFIG_SLIRP - case QEMU_OPTION_smb: - if (net_slirp_smb(optarg) < 0) - exit(1); - break; -#endif case QEMU_OPTION_runas: user_pwd = getpwnam(optarg); - if (!user_pwd) { - fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); + if (user_pwd) { + user_uid = -1; + user_gid = -1; + } else if (!os_parse_runas_uid_gid(optarg)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); exit(1); } break; @@ -164,27 +192,45 @@ void os_parse_cmd_args(int index, const char *optarg) fips_set_state(true); break; #endif + default: + return -1; } + + return 0; } static void change_process_uid(void) { - if (user_pwd) { - if (setgid(user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + assert((user_uid == (uid_t)-1) || user_pwd == NULL); + assert((user_uid == (uid_t)-1) == + (user_gid == (gid_t)-1)); + + if (user_pwd || user_uid != (uid_t)-1) { + gid_t intended_gid = user_pwd ? user_pwd->pw_gid : user_gid; + uid_t intended_uid = user_pwd ? user_pwd->pw_uid : user_uid; + if (setgid(intended_gid) < 0) { + error_report("Failed to setgid(%d)", intended_gid); exit(1); } - if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to initgroups(\"%s\", %d)\n", - user_pwd->pw_name, user_pwd->pw_gid); - exit(1); + if (user_pwd) { + if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { + error_report("Failed to initgroups(\"%s\", %d)", + user_pwd->pw_name, user_pwd->pw_gid); + exit(1); + } + } else { + if (setgroups(1, &user_gid) < 0) { + error_report("Failed to setgroups(1, [%d])", + user_gid); + exit(1); + } } - if (setuid(user_pwd->pw_uid) < 0) { - fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + if (setuid(intended_uid) < 0) { + error_report("Failed to setuid(%d)", intended_uid); exit(1); } if (setuid(0) != -1) { - fprintf(stderr, "Dropping privileges failed\n"); + error_report("Dropping privileges failed"); exit(1); } } @@ -194,11 +240,11 @@ static void change_root(void) { if (chroot_dir) { if (chroot(chroot_dir) < 0) { - fprintf(stderr, "chroot failed\n"); + error_report("chroot failed"); exit(1); } if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } } @@ -208,45 +254,45 @@ static void change_root(void) void os_daemonize(void) { if (daemonize) { - pid_t pid; + pid_t pid; + int fds[2]; + + if (pipe(fds) == -1) { + exit(1); + } - if (pipe(fds) == -1) - exit(1); + pid = fork(); + if (pid > 0) { + uint8_t status; + ssize_t len; - pid = fork(); - if (pid > 0) { - uint8_t status; - ssize_t len; + close(fds[1]); - close(fds[1]); + do { + len = read(fds[0], &status, 1); + } while (len < 0 && errno == EINTR); - again: - len = read(fds[0], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again; + /* only exit successfully if our child actually wrote + * a one-byte zero to our pipe, upon successful init */ + exit(len == 1 && status == 0 ? 0 : 1); - if (len != 1) - exit(1); - else if (status == 1) { - fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno)); - exit(1); - } else - exit(0); - } else if (pid < 0) + } else if (pid < 0) { exit(1); + } - close(fds[0]); - qemu_set_cloexec(fds[1]); - - setsid(); + close(fds[0]); + daemon_pipe = fds[1]; + qemu_set_cloexec(daemon_pipe); - pid = fork(); - if (pid > 0) - exit(0); - else if (pid < 0) - exit(1); + setsid(); - umask(027); + pid = fork(); + if (pid > 0) { + exit(0); + } else if (pid < 0) { + exit(1); + } + umask(027); signal(SIGTSTP, SIG_IGN); signal(SIGTTOU, SIG_IGN); @@ -259,47 +305,39 @@ void os_setup_post(void) int fd = 0; if (daemonize) { - uint8_t status = 0; - ssize_t len; - - again1: - len = write(fds[1], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again1; - - if (len != 1) - exit(1); - if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); + exit(1); + } + TFR(fd = qemu_open("/dev/null", O_RDWR)); + if (fd == -1) { exit(1); } - TFR(fd = qemu_open("/dev/null", O_RDWR)); - if (fd == -1) - exit(1); } change_root(); change_process_uid(); if (daemonize) { + uint8_t status = 0; + ssize_t len; + dup2(fd, 0); dup2(fd, 1); - dup2(fd, 2); + /* In case -D is given do not redirect stderr to /dev/null */ + if (!qemu_logfile) { + dup2(fd, 2); + } close(fd); - } -} -void os_pidfile_error(void) -{ - if (daemonize) { - uint8_t status = 1; - if (write(fds[1], &status, 1) != 1) { - perror("daemonize. Writing to pipe\n"); + do { + len = write(daemon_pipe, &status, 1); + } while (len < 0 && errno == EINTR); + if (len != 1) { + exit(1); } - } else - fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); + } } void os_set_line_buffering(void) @@ -307,30 +345,6 @@ void os_set_line_buffering(void) setvbuf(stdout, NULL, _IOLBF, 0); } -int qemu_create_pidfile(const char *filename) -{ - char buffer[128]; - int len; - int fd; - - fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); - if (fd == -1) { - return -1; - } - if (lockf(fd, F_TLOCK, 0) == -1) { - close(fd); - return -1; - } - len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid()); - if (write(fd, buffer, len) != len) { - close(fd); - return -1; - } - - /* keep pidfile open & locked forever */ - return 0; -} - bool is_daemonized(void) { return daemonize; @@ -342,7 +356,7 @@ int os_mlock(void) ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { - perror("mlockall"); + error_report("mlockall: %s", strerror(errno)); } return ret;