Initial public busybox upstream commit
[busybox4maemo] / 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 Denys Vlasenko <vda.linux@googlemail.com> */
29 /* Dependencies on runit_lib.c removed */
30
31 #include "libbb.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 struct globals {
43         char *set_user;
44         char *env_user;
45         const char *env_dir;
46         const char *root;
47         long limitd; /* limitX are initialized to -2 */
48         long limits;
49         long limitl;
50         long limita;
51         long limito;
52         long limitp;
53         long limitf;
54         long limitc;
55         long limitr;
56         long limitt;
57         int nicelvl;
58 };
59 #define G (*(struct globals*)&bb_common_bufsiz1)
60 #define set_user (G.set_user)
61 #define env_user (G.env_user)
62 #define env_dir  (G.env_dir )
63 #define root     (G.root    )
64 #define limitd   (G.limitd  )
65 #define limits   (G.limits  )
66 #define limitl   (G.limitl  )
67 #define limita   (G.limita  )
68 #define limito   (G.limito  )
69 #define limitp   (G.limitp  )
70 #define limitf   (G.limitf  )
71 #define limitc   (G.limitc  )
72 #define limitr   (G.limitr  )
73 #define limitt   (G.limitt  )
74 #define nicelvl  (G.nicelvl )
75 #define INIT_G() do { \
76         long *p = &limitd; \
77         do *p++ = -2; while (p <= &limitt); \
78 } while (0)
79
80 static void suidgid(char *user)
81 {
82         struct bb_uidgid_t ugid;
83
84         if (!get_uidgid(&ugid, user, 1)) {
85                 bb_error_msg_and_die("unknown user/group: %s", user);
86         }
87         if (setgroups(1, &ugid.gid) == -1)
88                 bb_perror_msg_and_die("setgroups");
89         xsetgid(ugid.gid);
90         xsetuid(ugid.uid);
91 }
92
93 static void euidgid(char *user)
94 {
95         struct bb_uidgid_t ugid;
96
97         if (!get_uidgid(&ugid, user, 1)) {
98                 bb_error_msg_and_die("unknown user/group: %s", user);
99         }
100         xsetenv("GID", utoa(ugid.gid));
101         xsetenv("UID", utoa(ugid.uid));
102 }
103
104 static void edir(const char *directory_name)
105 {
106         int wdir;
107         DIR *dir;
108         struct dirent *d;
109         int fd;
110
111         wdir = xopen(".", O_RDONLY | O_NDELAY);
112         xchdir(directory_name);
113         dir = opendir(".");
114         if (!dir)
115                 bb_perror_msg_and_die("opendir %s", directory_name);
116         for (;;) {
117                 errno = 0;
118                 d = readdir(dir);
119                 if (!d) {
120                         if (errno)
121                                 bb_perror_msg_and_die("readdir %s",
122                                                 directory_name);
123                         break;
124                 }
125                 if (d->d_name[0] == '.')
126                         continue;
127                 fd = open(d->d_name, O_RDONLY | O_NDELAY);
128                 if (fd < 0) {
129                         if ((errno == EISDIR) && env_dir) {
130                                 if (OPT_verbose)
131                                         bb_perror_msg("warning: %s/%s is a directory",
132                                                 directory_name, d->d_name);
133                                 continue;
134                         } else
135                                 bb_perror_msg_and_die("open %s/%s",
136                                                 directory_name, d->d_name);
137                 }
138                 if (fd >= 0) {
139                         char buf[256];
140                         char *tail;
141                         int size;
142
143                         size = safe_read(fd, buf, sizeof(buf)-1);
144                         if (size < 0)
145                                 bb_perror_msg_and_die("read %s/%s",
146                                                 directory_name, d->d_name);
147                         if (size == 0) {
148                                 unsetenv(d->d_name);
149                                 continue;
150                         }
151                         buf[size] = '\n';
152                         tail = memchr(buf, '\n', sizeof(buf));
153                         /* skip trailing whitespace */;
154                         while (1) {
155                                 if (tail[0] == ' ') tail[0] = '\0';
156                                 if (tail[0] == '\t') tail[0] = '\0';
157                                 if (tail[0] == '\n') tail[0] = '\0';
158                                 if (tail == buf) break;
159                                 tail--;
160                         }
161                         xsetenv(d->d_name, buf);
162                 }
163         }
164         closedir(dir);
165         if (fchdir(wdir) == -1)
166                 bb_perror_msg_and_die("fchdir");
167         close(wdir);
168 }
169
170 static void limit(int what, long l)
171 {
172         struct rlimit r;
173
174         /* Never fails under Linux (except if you pass it bad arguments) */
175         getrlimit(what, &r);
176         if ((l < 0) || (l > r.rlim_max))
177                 r.rlim_cur = r.rlim_max;
178         else
179                 r.rlim_cur = l;
180         if (setrlimit(what, &r) == -1)
181                 bb_perror_msg_and_die("setrlimit");
182 }
183
184 static void slimit(void)
185 {
186         if (limitd >= -1) {
187 #ifdef RLIMIT_DATA
188                 limit(RLIMIT_DATA, limitd);
189 #else
190                 if (OPT_verbose)
191                         bb_error_msg("system does not support RLIMIT_%s",
192                                 "DATA");
193 #endif
194         }
195         if (limits >= -1) {
196 #ifdef RLIMIT_STACK
197                 limit(RLIMIT_STACK, limits);
198 #else
199                 if (OPT_verbose)
200                         bb_error_msg("system does not support RLIMIT_%s",
201                                 "STACK");
202 #endif
203         }
204         if (limitl >= -1) {
205 #ifdef RLIMIT_MEMLOCK
206                 limit(RLIMIT_MEMLOCK, limitl);
207 #else
208                 if (OPT_verbose)
209                         bb_error_msg("system does not support RLIMIT_%s",
210                                 "MEMLOCK");
211 #endif
212         }
213         if (limita >= -1) {
214 #ifdef RLIMIT_VMEM
215                 limit(RLIMIT_VMEM, limita);
216 #else
217 #ifdef RLIMIT_AS
218                 limit(RLIMIT_AS, limita);
219 #else
220                 if (OPT_verbose)
221                         bb_error_msg("system does not support RLIMIT_%s",
222                                 "VMEM");
223 #endif
224 #endif
225         }
226         if (limito >= -1) {
227 #ifdef RLIMIT_NOFILE
228                 limit(RLIMIT_NOFILE, limito);
229 #else
230 #ifdef RLIMIT_OFILE
231                 limit(RLIMIT_OFILE, limito);
232 #else
233                 if (OPT_verbose)
234                         bb_error_msg("system does not support RLIMIT_%s",
235                                 "NOFILE");
236 #endif
237 #endif
238         }
239         if (limitp >= -1) {
240 #ifdef RLIMIT_NPROC
241                 limit(RLIMIT_NPROC, limitp);
242 #else
243                 if (OPT_verbose)
244                         bb_error_msg("system does not support RLIMIT_%s",
245                                 "NPROC");
246 #endif
247         }
248         if (limitf >= -1) {
249 #ifdef RLIMIT_FSIZE
250                 limit(RLIMIT_FSIZE, limitf);
251 #else
252                 if (OPT_verbose)
253                         bb_error_msg("system does not support RLIMIT_%s",
254                                 "FSIZE");
255 #endif
256         }
257         if (limitc >= -1) {
258 #ifdef RLIMIT_CORE
259                 limit(RLIMIT_CORE, limitc);
260 #else
261                 if (OPT_verbose)
262                         bb_error_msg("system does not support RLIMIT_%s",
263                                 "CORE");
264 #endif
265         }
266         if (limitr >= -1) {
267 #ifdef RLIMIT_RSS
268                 limit(RLIMIT_RSS, limitr);
269 #else
270                 if (OPT_verbose)
271                         bb_error_msg("system does not support RLIMIT_%s",
272                                 "RSS");
273 #endif
274         }
275         if (limitt >= -1) {
276 #ifdef RLIMIT_CPU
277                 limit(RLIMIT_CPU, limitt);
278 #else
279                 if (OPT_verbose)
280                         bb_error_msg("system does not support RLIMIT_%s",
281                                 "CPU");
282 #endif
283         }
284 }
285
286 /* argv[0] */
287 static void setuidgid(int, char **) ATTRIBUTE_NORETURN;
288 static void envuidgid(int, char **) ATTRIBUTE_NORETURN;
289 static void envdir(int, char **) ATTRIBUTE_NORETURN;
290 static void softlimit(int, char **) ATTRIBUTE_NORETURN;
291
292 int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
293 int chpst_main(int argc ATTRIBUTE_UNUSED, char **argv)
294 {
295         INIT_G();
296
297         if (applet_name[3] == 'd') envdir(argc, argv);
298         if (applet_name[1] == 'o') softlimit(argc, argv);
299         if (applet_name[0] == 's') setuidgid(argc, argv);
300         if (applet_name[0] == 'e') envuidgid(argc, argv);
301         // otherwise we are chpst
302
303         {
304                 char *m,*d,*o,*p,*f,*c,*r,*t,*n;
305                 getopt32(argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
306                                 &set_user,&env_user,&env_dir,
307                                 &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
308                 // if (option_mask32 & 0x1) // -u
309                 // if (option_mask32 & 0x2) // -U
310                 // if (option_mask32 & 0x4) // -e
311                 if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
312                 if (option_mask32 & 0x10) limitd = xatoul(d); // -d
313                 if (option_mask32 & 0x20) limito = xatoul(o); // -o
314                 if (option_mask32 & 0x40) limitp = xatoul(p); // -p
315                 if (option_mask32 & 0x80) limitf = xatoul(f); // -f
316                 if (option_mask32 & 0x100) limitc = xatoul(c); // -c
317                 if (option_mask32 & 0x200) limitr = xatoul(r); // -r
318                 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
319                 // if (option_mask32 & 0x800) // -/
320                 if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
321                 // The below consts should match #defines at top!
322                 //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
323                 //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
324                 //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
325                 //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
326                 //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
327         }
328         argv += optind;
329         if (!argv || !*argv) bb_show_usage();
330
331         if (OPT_pgrp) setsid();
332         if (env_dir) edir(env_dir);
333         if (root) {
334                 xchdir(root);
335                 xchroot(".");
336         }
337         slimit();
338         if (nicelvl) {
339                 errno = 0;
340                 if (nice(nicelvl) == -1)
341                         bb_perror_msg_and_die("nice");
342         }
343         if (env_user) euidgid(env_user);
344         if (set_user) suidgid(set_user);
345         if (OPT_nostdin) close(0);
346         if (OPT_nostdout) close(1);
347         if (OPT_nostderr) close(2);
348         BB_EXECVP(argv[0], argv);
349         bb_perror_msg_and_die("exec %s", argv[0]);
350 }
351
352 static void setuidgid(int argc ATTRIBUTE_UNUSED, char **argv)
353 {
354         const char *account;
355
356         account = *++argv;
357         if (!account) bb_show_usage();
358         if (!*++argv) bb_show_usage();
359         suidgid((char*)account);
360         BB_EXECVP(argv[0], argv);
361         bb_perror_msg_and_die("exec %s", argv[0]);
362 }
363
364 static void envuidgid(int argc ATTRIBUTE_UNUSED, char **argv)
365 {
366         const char *account;
367
368         account = *++argv;
369         if (!account) bb_show_usage();
370         if (!*++argv) bb_show_usage();
371         euidgid((char*)account);
372         BB_EXECVP(argv[0], argv);
373         bb_perror_msg_and_die("exec %s", argv[0]);
374 }
375
376 static void envdir(int argc ATTRIBUTE_UNUSED, char **argv)
377 {
378         const char *dir;
379
380         dir = *++argv;
381         if (!dir) bb_show_usage();
382         if (!*++argv) bb_show_usage();
383         edir(dir);
384         BB_EXECVP(argv[0], argv);
385         bb_perror_msg_and_die("exec %s", argv[0]);
386 }
387
388 static void softlimit(int argc ATTRIBUTE_UNUSED, char **argv)
389 {
390         char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
391         getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:",
392                         &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
393         if (option_mask32 & 0x001) limita = xatoul(a); // -a
394         if (option_mask32 & 0x002) limitc = xatoul(c); // -c
395         if (option_mask32 & 0x004) limitd = xatoul(d); // -d
396         if (option_mask32 & 0x008) limitf = xatoul(f); // -f
397         if (option_mask32 & 0x010) limitl = xatoul(l); // -l
398         if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
399         if (option_mask32 & 0x040) limito = xatoul(o); // -o
400         if (option_mask32 & 0x080) limitp = xatoul(p); // -p
401         if (option_mask32 & 0x100) limitr = xatoul(r); // -r
402         if (option_mask32 & 0x200) limits = xatoul(s); // -s
403         if (option_mask32 & 0x400) limitt = xatoul(t); // -t
404         argv += optind;
405         if (!argv[0]) bb_show_usage();
406         slimit();
407         BB_EXECVP(argv[0], argv);
408         bb_perror_msg_and_die("exec %s", argv[0]);
409 }