xref: /openbmc/qemu/os-posix.c (revision 02f5360d80a7a45190e31b8255b60edaf101211c)
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 "sysemu/runstate.h"
36  #include "qemu/cutils.h"
37  
38  #ifdef CONFIG_LINUX
39  #include <sys/prctl.h>
40  #endif
41  
42  
43  void 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  
52  static void termsig_handler(int signal, siginfo_t *info, void *c)
53  {
54      qemu_system_killed(info->si_signo, info->si_pid);
55  }
56  
57  void 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  
69  void 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   */
93  static struct passwd *user_pwd;    /*   NULL   non-NULL   NULL   */
94  static uid_t user_uid = (uid_t)-1; /*   -1      -1        >=0    */
95  static 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   */
104  bool 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  
137  static 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  
175  static const char *chroot_dir;
176  
177  void os_set_chroot(const char *path)
178  {
179      chroot_dir = path;
180  }
181  
182  static 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  
198  static int daemonize;
199  static int daemon_pipe;
200  
201  bool is_daemonized(void)
202  {
203      return daemonize;
204  }
205  
206  int os_set_daemonize(bool d)
207  {
208      daemonize = d;
209      return 0;
210  }
211  
212  void 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  
260  void 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  
285  void 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  
325  void os_set_line_buffering(void)
326  {
327      setvbuf(stdout, NULL, _IOLBF, 0);
328  }
329  
330  int os_mlock(void)
331  {
332  #ifdef HAVE_MLOCKALL
333      int ret = 0;
334  
335      ret = mlockall(MCL_CURRENT | MCL_FUTURE);
336      if (ret < 0) {
337          error_report("mlockall: %s", strerror(errno));
338      }
339  
340      return ret;
341  #else
342      return -ENOSYS;
343  #endif
344  }
345