14b8b5a157ae59e680a606196bbda3ab5dd084df
[oweals/busybox.git] / runit / chpst.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8    1. Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10    2. Redistributions in binary form must reproduce the above copyright
11       notice, this list of conditions and the following disclaimer in the
12       documentation and/or other materials provided with the distribution.
13    3. The name of the author may not be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29 /* Dependencies on runit_lib.c removed */
30
31 #include "busybox.h"
32
33 #include <dirent.h>
34
35 // Must match constants in chpst_main!
36 #define OPT_verbose  (option_mask32 & 0x2000)
37 #define OPT_pgrp     (option_mask32 & 0x4000)
38 #define OPT_nostdin  (option_mask32 & 0x8000)
39 #define OPT_nostdout (option_mask32 & 0x10000)
40 #define OPT_nostderr (option_mask32 & 0x20000)
41
42 static char *set_user;
43 static char *env_user;
44 static const char *env_dir;
45 static long limitd = -2;
46 static long limits = -2;
47 static long limitl = -2;
48 static long limita = -2;
49 static long limito = -2;
50 static long limitp = -2;
51 static long limitf = -2;
52 static long limitc = -2;
53 static long limitr = -2;
54 static long limitt = -2;
55 static int nicelvl;
56 static const char *root;
57
58 static void suidgid(char *user)
59 {
60         struct bb_uidgid_t ugid;
61
62         if (!get_uidgid(&ugid, user, 1)) {
63                 bb_error_msg_and_die("unknown user/group: %s", user);
64         }
65         if (setgroups(1, &ugid.gid) == -1)
66                 bb_perror_msg_and_die("setgroups");
67         xsetgid(ugid.gid);
68         xsetuid(ugid.uid);
69 }
70
71 static void euidgid(char *user)
72 {
73         struct bb_uidgid_t ugid;
74
75         if (!get_uidgid(&ugid, user, 1)) {
76                 bb_error_msg_and_die("unknown user/group: %s", user);
77         }
78         xsetenv("GID", utoa(ugid.gid));
79         xsetenv("UID", utoa(ugid.uid));
80 }
81
82 static void edir(const char *directory_name)
83 {
84         int wdir;
85         DIR *dir;
86         struct dirent *d;
87         int fd;
88
89         wdir = xopen(".", O_RDONLY | O_NDELAY);
90         xchdir(directory_name);
91         dir = opendir(".");
92         if (!dir)
93                 bb_perror_msg_and_die("opendir %s", directory_name);
94         for (;;) {
95                 errno = 0;
96                 d = readdir(dir);
97                 if (!d) {
98                         if (errno)
99                                 bb_perror_msg_and_die("readdir %s",
100                                                 directory_name);
101                         break;
102                 }
103                 if (d->d_name[0] == '.') continue;
104                 fd = open(d->d_name, O_RDONLY | O_NDELAY);
105                 if (fd < 0) {
106                         if ((errno == EISDIR) && env_dir) {
107                                 if (OPT_verbose)
108                                         bb_perror_msg("warning: %s/%s is a directory",
109                                                 directory_name, d->d_name);
110                                 continue;
111                         } else
112                                 bb_perror_msg_and_die("open %s/%s",
113                                                 directory_name, d->d_name);
114                 }
115                 if (fd >= 0) {
116                         char buf[256];
117                         char *tail;
118                         int size;
119
120                         size = safe_read(fd, buf, sizeof(buf)-1);
121                         if (size < 0)
122                                 bb_perror_msg_and_die("read %s/%s",
123                                                 directory_name, d->d_name);
124                         if (size == 0) {
125                                 unsetenv(d->d_name);
126                                 continue;
127                         }
128                         buf[size] = '\n';
129                         tail = memchr(buf, '\n', sizeof(buf));
130                         /* skip trailing whitespace */;
131                         while (1) {
132                                 if (tail[0]==' ') tail[0] = '\0';
133                                 if (tail[0]=='\t') tail[0] = '\0';
134                                 if (tail[0]=='\n') tail[0] = '\0';
135                                 if (tail == buf) break;
136                                 tail--;
137                         }
138                         xsetenv(d->d_name, buf);
139                 }
140         }
141         closedir(dir);
142         if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
143         close(wdir);
144 }
145
146 static void limit(int what, long l)
147 {
148         struct rlimit r;
149
150         if (getrlimit(what, &r) == -1) bb_perror_msg_and_die("getrlimit");
151         if ((l < 0) || (l > r.rlim_max))
152                 r.rlim_cur = r.rlim_max;
153         else
154                 r.rlim_cur = l;
155         if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit");
156 }
157
158 static void slimit(void)
159 {
160         if (limitd >= -1) {
161 #ifdef RLIMIT_DATA
162                 limit(RLIMIT_DATA, limitd);
163 #else
164                 if (OPT_verbose) bb_error_msg("system does not support %s",
165                                 "RLIMIT_DATA");
166 #endif
167         }
168         if (limits >= -1) {
169 #ifdef RLIMIT_STACK
170                 limit(RLIMIT_STACK, limits);
171 #else
172                 if (OPT_verbose) bb_error_msg("system does not support %s",
173                                 "RLIMIT_STACK");
174 #endif
175         }
176         if (limitl >= -1) {
177 #ifdef RLIMIT_MEMLOCK
178                 limit(RLIMIT_MEMLOCK, limitl);
179 #else
180                 if (OPT_verbose) bb_error_msg("system does not support %s",
181                                 "RLIMIT_MEMLOCK");
182 #endif
183         }
184         if (limita >= -1) {
185 #ifdef RLIMIT_VMEM
186                 limit(RLIMIT_VMEM, limita);
187 #else
188 #ifdef RLIMIT_AS
189                 limit(RLIMIT_AS, limita);
190 #else
191                 if (OPT_verbose)
192                         bb_error_msg("system does not support %s",
193                                 "RLIMIT_VMEM");
194 #endif
195 #endif
196         }
197         if (limito >= -1) {
198 #ifdef RLIMIT_NOFILE
199                 limit(RLIMIT_NOFILE, limito);
200 #else
201 #ifdef RLIMIT_OFILE
202                 limit(RLIMIT_OFILE, limito);
203 #else
204                 if (OPT_verbose)
205                         bb_error_msg("system does not support %s",
206                                 "RLIMIT_NOFILE");
207 #endif
208 #endif
209         }
210         if (limitp >= -1) {
211 #ifdef RLIMIT_NPROC
212                 limit(RLIMIT_NPROC, limitp);
213 #else
214                 if (OPT_verbose) bb_error_msg("system does not support %s",
215                                 "RLIMIT_NPROC");
216 #endif
217         }
218         if (limitf >= -1) {
219 #ifdef RLIMIT_FSIZE
220                 limit(RLIMIT_FSIZE, limitf);
221 #else
222                 if (OPT_verbose) bb_error_msg("system does not support %s",
223                                 "RLIMIT_FSIZE");
224 #endif
225         }
226         if (limitc >= -1) {
227 #ifdef RLIMIT_CORE
228                 limit(RLIMIT_CORE, limitc);
229 #else
230                 if (OPT_verbose) bb_error_msg("system does not support %s",
231                                 "RLIMIT_CORE");
232 #endif
233         }
234         if (limitr >= -1) {
235 #ifdef RLIMIT_RSS
236                 limit(RLIMIT_RSS, limitr);
237 #else
238                 if (OPT_verbose) bb_error_msg("system does not support %s",
239                                 "RLIMIT_RSS");
240 #endif
241         }
242         if (limitt >= -1) {
243 #ifdef RLIMIT_CPU
244                 limit(RLIMIT_CPU, limitt);
245 #else
246                 if (OPT_verbose) bb_error_msg("system does not support %s",
247                                 "RLIMIT_CPU");
248 #endif
249         }
250 }
251
252 /* argv[0] */
253 static void setuidgid(int, char **);
254 static void envuidgid(int, char **);
255 static void envdir(int, char **);
256 static void softlimit(int, char **);
257
258 int chpst_main(int argc, char **argv)
259 {
260         if (applet_name[3] == 'd') envdir(argc, argv);
261         if (applet_name[1] == 'o') softlimit(argc, argv);
262         if (applet_name[0] == 's') setuidgid(argc, argv);
263         if (applet_name[0] == 'e') envuidgid(argc, argv);
264         // otherwise we are chpst
265
266         {
267                 char *m,*d,*o,*p,*f,*c,*r,*t,*n;
268                 getopt32(argc, argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
269                                 &set_user,&env_user,&env_dir,
270                                 &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
271                 // if (option_mask32 & 0x1) // -u
272                 // if (option_mask32 & 0x2) // -U
273                 // if (option_mask32 & 0x4) // -e
274                 if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
275                 if (option_mask32 & 0x10) limitd = xatoul(d); // -d
276                 if (option_mask32 & 0x20) limito = xatoul(o); // -o
277                 if (option_mask32 & 0x40) limitp = xatoul(p); // -p
278                 if (option_mask32 & 0x80) limitf = xatoul(f); // -f
279                 if (option_mask32 & 0x100) limitc = xatoul(c); // -c
280                 if (option_mask32 & 0x200) limitr = xatoul(r); // -r
281                 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
282                 // if (option_mask32 & 0x800) // -/
283                 if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
284                 // The below consts should match #defines at top!
285                 //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
286                 //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
287                 //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
288                 //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
289                 //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
290         }
291         argv += optind;
292         if (!argv || !*argv) bb_show_usage();
293         
294         if (OPT_pgrp) setsid();
295         if (env_dir) edir(env_dir);
296         if (root) {
297                 xchdir(root);
298                 if (chroot(".") == -1)
299                         bb_perror_msg_and_die("chroot");
300         }
301         slimit();
302         if (nicelvl) {
303                 errno = 0;
304                 if (nice(nicelvl) == -1)
305                         bb_perror_msg_and_die("nice");
306         }
307         if (env_user) euidgid(env_user);
308         if (set_user) suidgid(set_user);
309         if (OPT_nostdin) close(0);
310         if (OPT_nostdout) close(1);
311         if (OPT_nostderr) close(2);
312         execvp(argv[0], argv);
313         bb_perror_msg_and_die("exec %s", argv[0]);
314 }
315
316 static void setuidgid(int argc, char **argv)
317 {
318         const char *account;
319
320         account = *++argv;
321         if (!account) bb_show_usage();
322         if (!*++argv) bb_show_usage();
323         suidgid((char*)account);
324         execvp(argv[0], argv);
325         bb_perror_msg_and_die("exec %s", argv[0]);
326 }
327
328 static void envuidgid(int argc, char **argv)
329 {
330         const char *account;
331
332         account = *++argv;
333         if (!account) bb_show_usage();
334         if (!*++argv) bb_show_usage();
335         euidgid((char*)account);
336         execvp(argv[0], argv);
337         bb_perror_msg_and_die("exec %s", argv[0]);
338 }
339
340 static void envdir(int argc, char **argv)
341 {
342         const char *dir;
343
344         dir = *++argv;
345         if (!dir) bb_show_usage();
346         if (!*++argv) bb_show_usage();
347         edir(dir);
348         execvp(argv[0], argv);
349         bb_perror_msg_and_die("exec %s", argv[0]);
350 }
351
352 static void softlimit(int argc, char **argv)
353 {
354         char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
355         getopt32(argc, argv, "+a:c:d:f:l:m:o:p:r:s:t:",
356                         &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
357         if (option_mask32 & 0x001) limita = xatoul(a); // -a
358         if (option_mask32 & 0x002) limitc = xatoul(c); // -c
359         if (option_mask32 & 0x004) limitd = xatoul(d); // -d
360         if (option_mask32 & 0x008) limitf = xatoul(f); // -f
361         if (option_mask32 & 0x010) limitl = xatoul(l); // -l
362         if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
363         if (option_mask32 & 0x040) limito = xatoul(o); // -o
364         if (option_mask32 & 0x080) limitp = xatoul(p); // -p
365         if (option_mask32 & 0x100) limitr = xatoul(r); // -r
366         if (option_mask32 & 0x200) limits = xatoul(s); // -s
367         if (option_mask32 & 0x400) limitt = xatoul(t); // -t
368         argv += optind;
369         if (!argv[0]) bb_show_usage();
370         slimit();
371         execvp(argv[0], argv);
372         bb_perror_msg_and_die("exec %s", argv[0]);
373 }