xref: /openbmc/qemu/os-posix.c (revision b6d32a06fc0984e537091cba08f2e1ed9f775d74)
186b645e7SJes Sorensen /*
286b645e7SJes Sorensen  * os-posix.c
386b645e7SJes Sorensen  *
486b645e7SJes Sorensen  * Copyright (c) 2003-2008 Fabrice Bellard
586b645e7SJes Sorensen  * Copyright (c) 2010 Red Hat, Inc.
686b645e7SJes Sorensen  *
786b645e7SJes Sorensen  * Permission is hereby granted, free of charge, to any person obtaining a copy
886b645e7SJes Sorensen  * of this software and associated documentation files (the "Software"), to deal
986b645e7SJes Sorensen  * in the Software without restriction, including without limitation the rights
1086b645e7SJes Sorensen  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1186b645e7SJes Sorensen  * copies of the Software, and to permit persons to whom the Software is
1286b645e7SJes Sorensen  * furnished to do so, subject to the following conditions:
1386b645e7SJes Sorensen  *
1486b645e7SJes Sorensen  * The above copyright notice and this permission notice shall be included in
1586b645e7SJes Sorensen  * all copies or substantial portions of the Software.
1686b645e7SJes Sorensen  *
1786b645e7SJes Sorensen  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1886b645e7SJes Sorensen  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1986b645e7SJes Sorensen  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2086b645e7SJes Sorensen  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2186b645e7SJes Sorensen  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2286b645e7SJes Sorensen  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2386b645e7SJes Sorensen  * THE SOFTWARE.
2486b645e7SJes Sorensen  */
2586b645e7SJes Sorensen 
26d38ea87aSPeter Maydell #include "qemu/osdep.h"
2703e471c4SFiona Ebner #include <sys/resource.h>
288d963e6aSJes Sorensen #include <sys/wait.h>
298847cfe8SJes Sorensen #include <pwd.h>
30cc4662f9SStefan Hajnoczi #include <grp.h>
316170540bSJes Sorensen #include <libgen.h>
3286b645e7SJes Sorensen 
33f853ac66SThomas Huth #include "qemu/error-report.h"
3496c33a45SDimitris Aragiorgis #include "qemu/log.h"
3554d31236SMarkus Armbruster #include "sysemu/runstate.h"
36f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
3786b645e7SJes Sorensen 
38ce798cf2SJes Sorensen #ifdef CONFIG_LINUX
39ce798cf2SJes Sorensen #include <sys/prctl.h>
40949d31e6SJes Sorensen #endif
41949d31e6SJes Sorensen 
428847cfe8SJes Sorensen 
os_setup_early_signal_handling(void)43fe98ac14SJes Sorensen void os_setup_early_signal_handling(void)
4486b645e7SJes Sorensen {
4586b645e7SJes Sorensen     struct sigaction act;
4686b645e7SJes Sorensen     sigfillset(&act.sa_mask);
4786b645e7SJes Sorensen     act.sa_flags = 0;
4886b645e7SJes Sorensen     act.sa_handler = SIG_IGN;
4986b645e7SJes Sorensen     sigaction(SIGPIPE, &act, NULL);
5086b645e7SJes Sorensen }
518d963e6aSJes Sorensen 
termsig_handler(int signal,siginfo_t * info,void * c)52f64622c4SGleb Natapov static void termsig_handler(int signal, siginfo_t *info, void *c)
538d963e6aSJes Sorensen {
54f64622c4SGleb Natapov     qemu_system_killed(info->si_signo, info->si_pid);
558d963e6aSJes Sorensen }
568d963e6aSJes Sorensen 
os_setup_signal_handling(void)578d963e6aSJes Sorensen void os_setup_signal_handling(void)
588d963e6aSJes Sorensen {
598d963e6aSJes Sorensen     struct sigaction act;
608d963e6aSJes Sorensen 
618d963e6aSJes Sorensen     memset(&act, 0, sizeof(act));
62f64622c4SGleb Natapov     act.sa_sigaction = termsig_handler;
63f64622c4SGleb Natapov     act.sa_flags = SA_SIGINFO;
648d963e6aSJes Sorensen     sigaction(SIGINT,  &act, NULL);
658d963e6aSJes Sorensen     sigaction(SIGHUP,  &act, NULL);
668d963e6aSJes Sorensen     sigaction(SIGTERM, &act, NULL);
678d963e6aSJes Sorensen }
686170540bSJes Sorensen 
os_set_proc_name(const char * s)69ce798cf2SJes Sorensen void os_set_proc_name(const char *s)
70ce798cf2SJes Sorensen {
71ce798cf2SJes Sorensen #if defined(PR_SET_NAME)
72ce798cf2SJes Sorensen     char name[16];
73ce798cf2SJes Sorensen     if (!s)
74ce798cf2SJes Sorensen         return;
753eadc68eSJim Meyering     pstrcpy(name, sizeof(name), s);
76ce798cf2SJes Sorensen     /* Could rewrite argv[0] too, but that's a bit more complicated.
77ce798cf2SJes Sorensen        This simple way is enough for `top'. */
78ce798cf2SJes Sorensen     if (prctl(PR_SET_NAME, name)) {
79a7aaec14SIan Jackson         error_report("unable to change process name: %s", strerror(errno));
80ce798cf2SJes Sorensen         exit(1);
81ce798cf2SJes Sorensen     }
82ce798cf2SJes Sorensen #else
8322cd4f48SIan Jackson     error_report("Change of process name not supported by your OS");
84ce798cf2SJes Sorensen     exit(1);
85ce798cf2SJes Sorensen #endif
86ce798cf2SJes Sorensen }
87ce798cf2SJes Sorensen 
88433aed5fSMichael Tokarev 
89433aed5fSMichael Tokarev /*
90433aed5fSMichael Tokarev  * Must set all three of these at once.
91433aed5fSMichael Tokarev  * Legal combinations are              unset   by name   by uid
92433aed5fSMichael Tokarev  */
93433aed5fSMichael Tokarev static struct passwd *user_pwd;    /*   NULL   non-NULL   NULL   */
94433aed5fSMichael Tokarev static uid_t user_uid = (uid_t)-1; /*   -1      -1        >=0    */
95433aed5fSMichael Tokarev static gid_t user_gid = (gid_t)-1; /*   -1      -1        >=0    */
96433aed5fSMichael Tokarev 
9722d02515SMichael Tokarev /*
98d2803376SPhilippe Mathieu-Daudé  * Prepare to change user ID. user_id can be one of 3 forms:
9922d02515SMichael Tokarev  *   - a username, in which case user ID will be changed to its uid,
10022d02515SMichael Tokarev  *     with primary and supplementary groups set up too;
10122d02515SMichael Tokarev  *   - a numeric uid, in which case only the uid will be set;
10222d02515SMichael Tokarev  *   - a pair of numeric uid:gid.
10322d02515SMichael Tokarev  */
os_set_runas(const char * user_id)104d2803376SPhilippe Mathieu-Daudé bool os_set_runas(const char *user_id)
1052c42f1e8SIan Jackson {
1062c42f1e8SIan Jackson     unsigned long lv;
1072c42f1e8SIan Jackson     const char *ep;
1082c42f1e8SIan Jackson     uid_t got_uid;
1092c42f1e8SIan Jackson     gid_t got_gid;
1102c42f1e8SIan Jackson     int rc;
1112c42f1e8SIan Jackson 
112d2803376SPhilippe Mathieu-Daudé     user_pwd = getpwnam(user_id);
11322d02515SMichael Tokarev     if (user_pwd) {
11422d02515SMichael Tokarev         user_uid = -1;
11522d02515SMichael Tokarev         user_gid = -1;
11622d02515SMichael Tokarev         return true;
11722d02515SMichael Tokarev     }
11822d02515SMichael Tokarev 
119d2803376SPhilippe Mathieu-Daudé     rc = qemu_strtoul(user_id, &ep, 0, &lv);
1202c42f1e8SIan Jackson     got_uid = lv; /* overflow here is ID in C99 */
1212c42f1e8SIan Jackson     if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) {
1222c42f1e8SIan Jackson         return false;
1232c42f1e8SIan Jackson     }
1242c42f1e8SIan Jackson 
1252c42f1e8SIan Jackson     rc = qemu_strtoul(ep + 1, 0, 0, &lv);
1262c42f1e8SIan Jackson     got_gid = lv; /* overflow here is ID in C99 */
1272c42f1e8SIan Jackson     if (rc || got_gid != lv || got_gid == (gid_t)-1) {
1282c42f1e8SIan Jackson         return false;
1292c42f1e8SIan Jackson     }
1302c42f1e8SIan Jackson 
1312c42f1e8SIan Jackson     user_pwd = NULL;
1322c42f1e8SIan Jackson     user_uid = got_uid;
1332c42f1e8SIan Jackson     user_gid = got_gid;
1342c42f1e8SIan Jackson     return true;
1352c42f1e8SIan Jackson }
1362c42f1e8SIan Jackson 
change_process_uid(void)137e06eb601SJes Sorensen static void change_process_uid(void)
1388847cfe8SJes Sorensen {
1392c42f1e8SIan Jackson     assert((user_uid == (uid_t)-1) || user_pwd == NULL);
1402c42f1e8SIan Jackson     assert((user_uid == (uid_t)-1) ==
1412c42f1e8SIan Jackson            (user_gid == (gid_t)-1));
1422c42f1e8SIan Jackson 
1432c42f1e8SIan Jackson     if (user_pwd || user_uid != (uid_t)-1) {
1442c42f1e8SIan Jackson         gid_t intended_gid = user_pwd ? user_pwd->pw_gid : user_gid;
1452c42f1e8SIan Jackson         uid_t intended_uid = user_pwd ? user_pwd->pw_uid : user_uid;
1462c42f1e8SIan Jackson         if (setgid(intended_gid) < 0) {
1472c42f1e8SIan Jackson             error_report("Failed to setgid(%d)", intended_gid);
1488847cfe8SJes Sorensen             exit(1);
1498847cfe8SJes Sorensen         }
1502c42f1e8SIan Jackson         if (user_pwd) {
151cc4662f9SStefan Hajnoczi             if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) {
152f0a2171bSIan Jackson                 error_report("Failed to initgroups(\"%s\", %d)",
153cc4662f9SStefan Hajnoczi                         user_pwd->pw_name, user_pwd->pw_gid);
154cc4662f9SStefan Hajnoczi                 exit(1);
155cc4662f9SStefan Hajnoczi             }
1562c42f1e8SIan Jackson         } else {
1572c42f1e8SIan Jackson             if (setgroups(1, &user_gid) < 0) {
1582c42f1e8SIan Jackson                 error_report("Failed to setgroups(1, [%d])",
1592c42f1e8SIan Jackson                         user_gid);
1602c42f1e8SIan Jackson                 exit(1);
1612c42f1e8SIan Jackson             }
1622c42f1e8SIan Jackson         }
1632c42f1e8SIan Jackson         if (setuid(intended_uid) < 0) {
1642c42f1e8SIan Jackson             error_report("Failed to setuid(%d)", intended_uid);
1658847cfe8SJes Sorensen             exit(1);
1668847cfe8SJes Sorensen         }
1678847cfe8SJes Sorensen         if (setuid(0) != -1) {
168f0a2171bSIan Jackson             error_report("Dropping privileges failed");
1698847cfe8SJes Sorensen             exit(1);
1708847cfe8SJes Sorensen         }
1718847cfe8SJes Sorensen     }
1728847cfe8SJes Sorensen }
1730766379dSJes Sorensen 
174433aed5fSMichael Tokarev 
175433aed5fSMichael Tokarev static const char *chroot_dir;
176433aed5fSMichael Tokarev 
os_set_chroot(const char * path)177d2803376SPhilippe Mathieu-Daudé void os_set_chroot(const char *path)
1785b156390SMichael Tokarev {
179d2803376SPhilippe Mathieu-Daudé     chroot_dir = path;
1805b156390SMichael Tokarev }
1815b156390SMichael Tokarev 
change_root(void)182e06eb601SJes Sorensen static void change_root(void)
1830766379dSJes Sorensen {
1840766379dSJes Sorensen     if (chroot_dir) {
1850766379dSJes Sorensen         if (chroot(chroot_dir) < 0) {
18622cd4f48SIan Jackson             error_report("chroot failed");
1870766379dSJes Sorensen             exit(1);
1880766379dSJes Sorensen         }
1890766379dSJes Sorensen         if (chdir("/")) {
190a7aaec14SIan Jackson             error_report("not able to chdir to /: %s", strerror(errno));
1910766379dSJes Sorensen             exit(1);
1920766379dSJes Sorensen         }
1930766379dSJes Sorensen     }
1940766379dSJes Sorensen 
1950766379dSJes Sorensen }
196eb505be1SJes Sorensen 
197433aed5fSMichael Tokarev 
198433aed5fSMichael Tokarev static int daemonize;
199433aed5fSMichael Tokarev static int daemon_pipe;
200433aed5fSMichael Tokarev 
is_daemonized(void)201433aed5fSMichael Tokarev bool is_daemonized(void)
202433aed5fSMichael Tokarev {
203433aed5fSMichael Tokarev     return daemonize;
204433aed5fSMichael Tokarev }
205433aed5fSMichael Tokarev 
os_set_daemonize(bool d)206433aed5fSMichael Tokarev int os_set_daemonize(bool d)
207433aed5fSMichael Tokarev {
208433aed5fSMichael Tokarev     daemonize = d;
209433aed5fSMichael Tokarev     return 0;
210433aed5fSMichael Tokarev }
211433aed5fSMichael Tokarev 
os_daemonize(void)212eb505be1SJes Sorensen void os_daemonize(void)
213eb505be1SJes Sorensen {
214eb505be1SJes Sorensen     if (daemonize) {
215eb505be1SJes Sorensen         pid_t pid;
2160be5e436SMichael Tokarev         int fds[2];
217eb505be1SJes Sorensen 
2183338a41fSMarc-André Lureau         if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) {
219eb505be1SJes Sorensen             exit(1);
22063ce8e15SGonglei         }
221eb505be1SJes Sorensen 
222eb505be1SJes Sorensen         pid = fork();
223eb505be1SJes Sorensen         if (pid > 0) {
224eb505be1SJes Sorensen             uint8_t status;
225eb505be1SJes Sorensen             ssize_t len;
226eb505be1SJes Sorensen 
227eb505be1SJes Sorensen             close(fds[1]);
228eb505be1SJes Sorensen 
229ccea25f1SMichael Tokarev             do {
230eb505be1SJes Sorensen                 len = read(fds[0], &status, 1);
231ccea25f1SMichael Tokarev             } while (len < 0 && errno == EINTR);
232fee78fd6SMichael Tokarev 
233fee78fd6SMichael Tokarev             /* only exit successfully if our child actually wrote
234fee78fd6SMichael Tokarev              * a one-byte zero to our pipe, upon successful init */
235fee78fd6SMichael Tokarev             exit(len == 1 && status == 0 ? 0 : 1);
236fee78fd6SMichael Tokarev 
23763ce8e15SGonglei         } else if (pid < 0) {
238eb505be1SJes Sorensen             exit(1);
23963ce8e15SGonglei         }
240eb505be1SJes Sorensen 
241eb505be1SJes Sorensen         close(fds[0]);
2420be5e436SMichael Tokarev         daemon_pipe = fds[1];
243eb505be1SJes Sorensen 
244eb505be1SJes Sorensen         setsid();
245eb505be1SJes Sorensen 
246eb505be1SJes Sorensen         pid = fork();
24763ce8e15SGonglei         if (pid > 0) {
248eb505be1SJes Sorensen             exit(0);
24963ce8e15SGonglei         } else if (pid < 0) {
250eb505be1SJes Sorensen             exit(1);
25163ce8e15SGonglei         }
252eb505be1SJes Sorensen         umask(027);
253eb505be1SJes Sorensen 
254eb505be1SJes Sorensen         signal(SIGTSTP, SIG_IGN);
255eb505be1SJes Sorensen         signal(SIGTTOU, SIG_IGN);
256eb505be1SJes Sorensen         signal(SIGTTIN, SIG_IGN);
257eb505be1SJes Sorensen     }
258eb505be1SJes Sorensen }
259eb505be1SJes Sorensen 
os_setup_limits(void)26003e471c4SFiona Ebner void os_setup_limits(void)
26103e471c4SFiona Ebner {
26203e471c4SFiona Ebner     struct rlimit nofile;
26303e471c4SFiona Ebner 
26403e471c4SFiona Ebner     if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) {
26503e471c4SFiona Ebner         warn_report("unable to query NOFILE limit: %s", strerror(errno));
26603e471c4SFiona Ebner         return;
26703e471c4SFiona Ebner     }
26803e471c4SFiona Ebner 
26903e471c4SFiona Ebner     if (nofile.rlim_cur == nofile.rlim_max) {
27003e471c4SFiona Ebner         return;
27103e471c4SFiona Ebner     }
27203e471c4SFiona Ebner 
273*de448e0fSTrent Huber #ifdef CONFIG_DARWIN
274*de448e0fSTrent Huber     nofile.rlim_cur = OPEN_MAX < nofile.rlim_max ? OPEN_MAX : nofile.rlim_max;
275*de448e0fSTrent Huber #else
27603e471c4SFiona Ebner     nofile.rlim_cur = nofile.rlim_max;
277*de448e0fSTrent Huber #endif
27803e471c4SFiona Ebner 
27903e471c4SFiona Ebner     if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) {
28003e471c4SFiona Ebner         warn_report("unable to set NOFILE limit: %s", strerror(errno));
28103e471c4SFiona Ebner         return;
28203e471c4SFiona Ebner     }
28303e471c4SFiona Ebner }
28403e471c4SFiona Ebner 
os_setup_post(void)285eb505be1SJes Sorensen void os_setup_post(void)
286eb505be1SJes Sorensen {
287eb505be1SJes Sorensen     int fd = 0;
288eb505be1SJes Sorensen 
289eb505be1SJes Sorensen     if (daemonize) {
290eb505be1SJes Sorensen         if (chdir("/")) {
291a7aaec14SIan Jackson             error_report("not able to chdir to /: %s", strerror(errno));
292eb505be1SJes Sorensen             exit(1);
293eb505be1SJes Sorensen         }
2948b6aa693SNikita Ivanov         fd = RETRY_ON_EINTR(qemu_open_old("/dev/null", O_RDWR));
29563ce8e15SGonglei         if (fd == -1) {
296eb505be1SJes Sorensen             exit(1);
297eb505be1SJes Sorensen         }
29863ce8e15SGonglei     }
299eb505be1SJes Sorensen 
300e06eb601SJes Sorensen     change_root();
301e06eb601SJes Sorensen     change_process_uid();
302eb505be1SJes Sorensen 
303eb505be1SJes Sorensen     if (daemonize) {
30425cec2b8SMichael Tokarev         uint8_t status = 0;
30525cec2b8SMichael Tokarev         ssize_t len;
30625cec2b8SMichael Tokarev 
307eb505be1SJes Sorensen         dup2(fd, 0);
308eb505be1SJes Sorensen         dup2(fd, 1);
30996c33a45SDimitris Aragiorgis         /* In case -D is given do not redirect stderr to /dev/null */
310229ef2ebSRichard Henderson         if (!qemu_log_enabled()) {
311eb505be1SJes Sorensen             dup2(fd, 2);
31296c33a45SDimitris Aragiorgis         }
313eb505be1SJes Sorensen 
314eb505be1SJes Sorensen         close(fd);
31525cec2b8SMichael Tokarev 
31625cec2b8SMichael Tokarev         do {
31725cec2b8SMichael Tokarev             len = write(daemon_pipe, &status, 1);
31825cec2b8SMichael Tokarev         } while (len < 0 && errno == EINTR);
31925cec2b8SMichael Tokarev         if (len != 1) {
32025cec2b8SMichael Tokarev             exit(1);
32125cec2b8SMichael Tokarev         }
322eb505be1SJes Sorensen     }
323eb505be1SJes Sorensen }
324eb505be1SJes Sorensen 
os_set_line_buffering(void)3259156d763SJes Sorensen void os_set_line_buffering(void)
3269156d763SJes Sorensen {
3279156d763SJes Sorensen     setvbuf(stdout, NULL, _IOLBF, 0);
3289156d763SJes Sorensen }
329949d31e6SJes Sorensen 
os_mlock(void)330888a6bc6SSatoru Moriya int os_mlock(void)
331888a6bc6SSatoru Moriya {
332195588ccSDavid CARLIER #ifdef HAVE_MLOCKALL
333888a6bc6SSatoru Moriya     int ret = 0;
334888a6bc6SSatoru Moriya 
335888a6bc6SSatoru Moriya     ret = mlockall(MCL_CURRENT | MCL_FUTURE);
336888a6bc6SSatoru Moriya     if (ret < 0) {
337a7aaec14SIan Jackson         error_report("mlockall: %s", strerror(errno));
338888a6bc6SSatoru Moriya     }
339888a6bc6SSatoru Moriya 
340888a6bc6SSatoru Moriya     return ret;
341195588ccSDavid CARLIER #else
342195588ccSDavid CARLIER     return -ENOSYS;
343195588ccSDavid CARLIER #endif
344888a6bc6SSatoru Moriya }
345