qemu/os-posix.c
<<
>>
Prefs
   1/*
   2 * os-posix.c
   3 *
   4 * Copyright (c) 2003-2008 Fabrice Bellard
   5 * Copyright (c) 2010 Red Hat, Inc.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include <sys/resource.h>
  28#include <sys/wait.h>
  29#include <pwd.h>
  30#include <grp.h>
  31#include <libgen.h>
  32
  33#include "qemu/error-report.h"
  34#include "qemu/log.h"
  35#include "system/runstate.h"
  36#include "qemu/cutils.h"
  37
  38#ifdef CONFIG_LINUX
  39#include <sys/prctl.h>
  40#endif
  41
  42
  43void os_setup_early_signal_handling(void)
  44{
  45    struct sigaction act;
  46    sigfillset(&act.sa_mask);
  47    act.sa_flags = 0;
  48    act.sa_handler = SIG_IGN;
  49    sigaction(SIGPIPE, &act, NULL);
  50}
  51
  52static void termsig_handler(int signal, siginfo_t *info, void *c)
  53{
  54    qemu_system_killed(info->si_signo, info->si_pid);
  55}
  56
  57void os_setup_signal_handling(void)
  58{
  59    struct sigaction act;
  60
  61    memset(&act, 0, sizeof(act));
  62    act.sa_sigaction = termsig_handler;
  63    act.sa_flags = SA_SIGINFO;
  64    sigaction(SIGINT,  &act, NULL);
  65    sigaction(SIGHUP,  &act, NULL);
  66    sigaction(SIGTERM, &act, NULL);
  67}
  68
  69void os_set_proc_name(const char *s)
  70{
  71#if defined(PR_SET_NAME)
  72    char name[16];
  73    if (!s)
  74        return;
  75    pstrcpy(name, sizeof(name), s);
  76    /* Could rewrite argv[0] too, but that's a bit more complicated.
  77       This simple way is enough for `top'. */
  78    if (prctl(PR_SET_NAME, name)) {
  79        error_report("unable to change process name: %s", strerror(errno));
  80        exit(1);
  81    }
  82#else
  83    error_report("Change of process name not supported by your OS");
  84    exit(1);
  85#endif
  86}
  87
  88
  89/*
  90 * Must set all three of these at once.
  91 * Legal combinations are              unset   by name   by uid
  92 */
  93static struct passwd *user_pwd;    /*   NULL   non-NULL   NULL   */
  94static uid_t user_uid = (uid_t)-1; /*   -1      -1        >=0    */
  95static gid_t user_gid = (gid_t)-1; /*   -1      -1        >=0    */
  96
  97/*
  98 * Prepare to change user ID. user_id can be one of 3 forms:
  99 *   - a username, in which case user ID will be changed to its uid,
 100 *     with primary and supplementary groups set up too;
 101 *   - a numeric uid, in which case only the uid will be set;
 102 *   - a pair of numeric uid:gid.
 103 */
 104bool os_set_runas(const char *user_id)
 105{
 106    unsigned long lv;
 107    const char *ep;
 108    uid_t got_uid;
 109    gid_t got_gid;
 110    int rc;
 111
 112    user_pwd = getpwnam(user_id);
 113    if (user_pwd) {
 114        user_uid = -1;
 115        user_gid = -1;
 116        return true;
 117    }
 118
 119    rc = qemu_strtoul(user_id, &ep, 0, &lv);
 120    got_uid = lv; /* overflow here is ID in C99 */
 121    if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) {
 122        return false;
 123    }
 124
 125    rc = qemu_strtoul(ep + 1, 0, 0, &lv);
 126    got_gid = lv; /* overflow here is ID in C99 */
 127    if (rc || got_gid != lv || got_gid == (gid_t)-1) {
 128        return false;
 129    }
 130
 131    user_pwd = NULL;
 132    user_uid = got_uid;
 133    user_gid = got_gid;
 134    return true;
 135}
 136
 137static void change_process_uid(void)
 138{
 139    assert((user_uid == (uid_t)-1) || user_pwd == NULL);
 140    assert((user_uid == (uid_t)-1) ==
 141           (user_gid == (gid_t)-1));
 142
 143    if (user_pwd || user_uid != (uid_t)-1) {
 144        gid_t intended_gid = user_pwd ? user_pwd->pw_gid : user_gid;
 145        uid_t intended_uid = user_pwd ? user_pwd->pw_uid : user_uid;
 146        if (setgid(intended_gid) < 0) {
 147            error_report("Failed to setgid(%d)", intended_gid);
 148            exit(1);
 149        }
 150        if (user_pwd) {
 151            if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) {
 152                error_report("Failed to initgroups(\"%s\", %d)",
 153                        user_pwd->pw_name, user_pwd->pw_gid);
 154                exit(1);
 155            }
 156        } else {
 157            if (setgroups(1, &user_gid) < 0) {
 158                error_report("Failed to setgroups(1, [%d])",
 159                        user_gid);
 160                exit(1);
 161            }
 162        }
 163        if (setuid(intended_uid) < 0) {
 164            error_report("Failed to setuid(%d)", intended_uid);
 165            exit(1);
 166        }
 167        if (setuid(0) != -1) {
 168            error_report("Dropping privileges failed");
 169            exit(1);
 170        }
 171    }
 172}
 173
 174
 175static const char *chroot_dir;
 176
 177void os_set_chroot(const char *path)
 178{
 179    chroot_dir = path;
 180}
 181
 182static void change_root(void)
 183{
 184    if (chroot_dir) {
 185        if (chroot(chroot_dir) < 0) {
 186            error_report("chroot failed");
 187            exit(1);
 188        }
 189        if (chdir("/")) {
 190            error_report("not able to chdir to /: %s", strerror(errno));
 191            exit(1);
 192        }
 193    }
 194
 195}
 196
 197
 198static int daemonize;
 199static int daemon_pipe;
 200
 201bool is_daemonized(void)
 202{
 203    return daemonize;
 204}
 205
 206int os_set_daemonize(bool d)
 207{
 208    daemonize = d;
 209    return 0;
 210}
 211
 212void os_daemonize(void)
 213{
 214    if (daemonize) {
 215        pid_t pid;
 216        int fds[2];
 217
 218        if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) {
 219            exit(1);
 220        }
 221
 222        pid = fork();
 223        if (pid > 0) {
 224            uint8_t status;
 225            ssize_t len;
 226
 227            close(fds[1]);
 228
 229            do {
 230                len = read(fds[0], &status, 1);
 231            } while (len < 0 && errno == EINTR);
 232
 233            /* only exit successfully if our child actually wrote
 234             * a one-byte zero to our pipe, upon successful init */
 235            exit(len == 1 && status == 0 ? 0 : 1);
 236
 237        } else if (pid < 0) {
 238            exit(1);
 239        }
 240
 241        close(fds[0]);
 242        daemon_pipe = fds[1];
 243
 244        setsid();
 245
 246        pid = fork();
 247        if (pid > 0) {
 248            exit(0);
 249        } else if (pid < 0) {
 250            exit(1);
 251        }
 252        umask(027);
 253
 254        signal(SIGTSTP, SIG_IGN);
 255        signal(SIGTTOU, SIG_IGN);
 256        signal(SIGTTIN, SIG_IGN);
 257    }
 258}
 259
 260void os_setup_limits(void)
 261{
 262    struct rlimit nofile;
 263
 264    if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) {
 265        warn_report("unable to query NOFILE limit: %s", strerror(errno));
 266        return;
 267    }
 268
 269    if (nofile.rlim_cur == nofile.rlim_max) {
 270        return;
 271    }
 272
 273#ifdef CONFIG_DARWIN
 274    nofile.rlim_cur = OPEN_MAX < nofile.rlim_max ? OPEN_MAX : nofile.rlim_max;
 275#else
 276    nofile.rlim_cur = nofile.rlim_max;
 277#endif
 278
 279    if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) {
 280        warn_report("unable to set NOFILE limit: %s", strerror(errno));
 281        return;
 282    }
 283}
 284
 285void os_setup_post(void)
 286{
 287    int fd = 0;
 288
 289    if (daemonize) {
 290        if (chdir("/")) {
 291            error_report("not able to chdir to /: %s", strerror(errno));
 292            exit(1);
 293        }
 294        fd = RETRY_ON_EINTR(qemu_open_old("/dev/null", O_RDWR));
 295        if (fd == -1) {
 296            exit(1);
 297        }
 298    }
 299
 300    change_root();
 301    change_process_uid();
 302
 303    if (daemonize) {
 304        uint8_t status = 0;
 305        ssize_t len;
 306
 307        dup2(fd, 0);
 308        dup2(fd, 1);
 309        /* In case -D is given do not redirect stderr to /dev/null */
 310        if (!qemu_log_enabled()) {
 311            dup2(fd, 2);
 312        }
 313
 314        close(fd);
 315
 316        do {        
 317            len = write(daemon_pipe, &status, 1);
 318        } while (len < 0 && errno == EINTR);
 319        if (len != 1) {
 320            exit(1);
 321        }
 322    }
 323}
 324
 325void os_set_line_buffering(void)
 326{
 327    setvbuf(stdout, NULL, _IOLBF, 0);
 328}
 329
 330int os_mlock(bool on_fault)
 331{
 332#ifdef HAVE_MLOCKALL
 333    int ret = 0;
 334    int flags = MCL_CURRENT | MCL_FUTURE;
 335
 336    if (on_fault) {
 337#ifdef HAVE_MLOCK_ONFAULT
 338        flags |= MCL_ONFAULT;
 339#else
 340        error_report("mlockall: on_fault not supported");
 341        return -EINVAL;
 342#endif
 343    }
 344
 345    ret = mlockall(flags);
 346    if (ret < 0) {
 347        error_report("mlockall: %s", strerror(errno));
 348    }
 349
 350    return ret;
 351#else
 352    (void)on_fault;
 353    return -ENOSYS;
 354#endif
 355}
 356