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