blob: e9b6276383cf8e3cd5ef5359a52786d07691e25a [file] [log] [blame]
Erik Andersen13456d12000-03-16 08:09:57 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen5e1189e2000-04-15 16:34:54 +00003 * test implementation for busybox
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (c) by a whole pile of folks:
Erik Andersen13456d12000-03-16 08:09:57 +00006 *
Paul Fox6ab03782006-06-08 21:37:26 +00007 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersen@codepoet.org> to be used
14 * in busybox.
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +000015 * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty).
Erik Andersen13456d12000-03-16 08:09:57 +000016 *
Paul Fox6ab03782006-06-08 21:37:26 +000017 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Erik Andersen13456d12000-03-16 08:09:57 +000018 *
19 * Original copyright notice states:
Paul Fox6ab03782006-06-08 21:37:26 +000020 * "This program is in the Public Domain."
Erik Andersen13456d12000-03-16 08:09:57 +000021 */
22
Rob Landleyea224be2006-06-18 20:20:07 +000023#include "busybox.h"
Paul Fox6ab03782006-06-08 21:37:26 +000024#include <setjmp.h>
Erik Andersen13456d12000-03-16 08:09:57 +000025
Denis Vlasenko99912ca2007-04-10 15:43:37 +000026/* This is a NOEXEC applet. Be very careful! */
27
28
Erik Andersen13456d12000-03-16 08:09:57 +000029/* test(1) accepts the following grammar:
30 oexpr ::= aexpr | aexpr "-o" oexpr ;
31 aexpr ::= nexpr | nexpr "-a" aexpr ;
32 nexpr ::= primary | "!" primary
Paul Fox6ab03782006-06-08 21:37:26 +000033 primary ::= unary-operator operand
Erik Andersen13456d12000-03-16 08:09:57 +000034 | operand binary-operator operand
35 | operand
36 | "(" oexpr ")"
37 ;
38 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
39 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
40
Mike Frysinger75ac42b2005-04-14 02:49:22 +000041 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
Erik Andersen13456d12000-03-16 08:09:57 +000042 "-nt"|"-ot"|"-ef";
43 operand ::= <any legal UNIX file name>
44*/
45
46enum token {
47 EOI,
48 FILRD,
49 FILWR,
50 FILEX,
51 FILEXIST,
52 FILREG,
53 FILDIR,
54 FILCDEV,
55 FILBDEV,
56 FILFIFO,
57 FILSOCK,
58 FILSYM,
59 FILGZ,
60 FILTT,
61 FILSUID,
62 FILSGID,
63 FILSTCK,
64 FILNT,
65 FILOT,
66 FILEQ,
67 FILUID,
68 FILGID,
69 STREZ,
70 STRNZ,
71 STREQ,
72 STRNE,
73 STRLT,
74 STRGT,
75 INTEQ,
76 INTNE,
77 INTGE,
78 INTGT,
79 INTLE,
80 INTLT,
81 UNOT,
82 BAND,
83 BOR,
84 LPAREN,
85 RPAREN,
86 OPERAND
87};
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +000088#define __is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
89#define __is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
90#define __is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
91#define __is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
92#define __is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
93#define __is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
Erik Andersen13456d12000-03-16 08:09:57 +000094enum token_types {
95 UNOP,
96 BINOP,
97 BUNOP,
98 BBINOP,
99 PAREN
100};
101
Eric Andersen92d23242001-03-19 23:49:41 +0000102static const struct t_op {
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000103 const char * const op_text;
104 unsigned char op_num, op_type;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000105} ops[] = {
106 {
107 "-r", FILRD, UNOP}, {
108 "-w", FILWR, UNOP}, {
109 "-x", FILEX, UNOP}, {
110 "-e", FILEXIST, UNOP}, {
111 "-f", FILREG, UNOP}, {
112 "-d", FILDIR, UNOP}, {
113 "-c", FILCDEV, UNOP}, {
114 "-b", FILBDEV, UNOP}, {
115 "-p", FILFIFO, UNOP}, {
116 "-u", FILSUID, UNOP}, {
117 "-g", FILSGID, UNOP}, {
118 "-k", FILSTCK, UNOP}, {
119 "-s", FILGZ, UNOP}, {
120 "-t", FILTT, UNOP}, {
121 "-z", STREZ, UNOP}, {
122 "-n", STRNZ, UNOP}, {
Paul Fox6ab03782006-06-08 21:37:26 +0000123 "-h", FILSYM, UNOP}, /* for backwards compat */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000124 {
125 "-O", FILUID, UNOP}, {
126 "-G", FILGID, UNOP}, {
127 "-L", FILSYM, UNOP}, {
128 "-S", FILSOCK, UNOP}, {
129 "=", STREQ, BINOP}, {
Mike Frysinger75ac42b2005-04-14 02:49:22 +0000130 "==", STREQ, BINOP}, {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000131 "!=", STRNE, BINOP}, {
132 "<", STRLT, BINOP}, {
133 ">", STRGT, BINOP}, {
134 "-eq", INTEQ, BINOP}, {
135 "-ne", INTNE, BINOP}, {
136 "-ge", INTGE, BINOP}, {
137 "-gt", INTGT, BINOP}, {
138 "-le", INTLE, BINOP}, {
139 "-lt", INTLT, BINOP}, {
140 "-nt", FILNT, BINOP}, {
141 "-ot", FILOT, BINOP}, {
142 "-ef", FILEQ, BINOP}, {
143 "!", UNOT, BUNOP}, {
144 "-a", BAND, BBINOP}, {
145 "-o", BOR, BBINOP}, {
146 "(", LPAREN, PAREN}, {
147 ")", RPAREN, PAREN}, {
148 0, 0, 0}
Erik Andersen13456d12000-03-16 08:09:57 +0000149};
150
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000151#ifdef CONFIG_FEATURE_TEST_64
152typedef int64_t arith_t;
153#else
154typedef int arith_t;
155#endif
156
Matt Kraaie6e81832000-12-30 07:46:23 +0000157static char **t_wp;
158static struct t_op const *t_wp_op;
Rob Landleyecb29572006-08-22 23:40:28 +0000159static gid_t *group_array;
Erik Andersen13456d12000-03-16 08:09:57 +0000160static int ngroups;
161
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000162static enum token t_lex(char *s);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000163static arith_t oexpr(enum token n);
164static arith_t aexpr(enum token n);
165static arith_t nexpr(enum token n);
Eric Andersen74400cc2001-10-18 04:11:39 +0000166static int binop(void);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000167static arith_t primary(enum token n);
Eric Andersen74400cc2001-10-18 04:11:39 +0000168static int filstat(char *nm, enum token mode);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000169static arith_t getn(const char *s);
Denis Vlasenko729bd9e2007-04-02 12:37:28 +0000170/* UNUSED
Eric Andersen74400cc2001-10-18 04:11:39 +0000171static int newerf(const char *f1, const char *f2);
172static int olderf(const char *f1, const char *f2);
173static int equalf(const char *f1, const char *f2);
Denis Vlasenko729bd9e2007-04-02 12:37:28 +0000174*/
Eric Andersen74400cc2001-10-18 04:11:39 +0000175static int test_eaccess(char *path, int mode);
176static int is_a_group_member(gid_t gid);
177static void initialize_group_array(void);
Erik Andersen13456d12000-03-16 08:09:57 +0000178
Paul Fox6ab03782006-06-08 21:37:26 +0000179static jmp_buf leaving;
180
181int bb_test(int argc, char **argv)
Erik Andersen13456d12000-03-16 08:09:57 +0000182{
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000183 int res;
Erik Andersen13456d12000-03-16 08:09:57 +0000184
Denis Vlasenkobf66fbc2006-12-21 13:23:14 +0000185 if (LONE_CHAR(argv[0], '[')) {
186 --argc;
187 if (NOT_LONE_CHAR(argv[argc], ']')) {
Paul Fox6ab03782006-06-08 21:37:26 +0000188 bb_error_msg("missing ]");
189 return 2;
190 }
191 argv[argc] = NULL;
192 } else if (strcmp(argv[0], "[[") == 0) {
Denis Vlasenkobf66fbc2006-12-21 13:23:14 +0000193 --argc;
194 if (strcmp(argv[argc], "]]")) {
Paul Fox6ab03782006-06-08 21:37:26 +0000195 bb_error_msg("missing ]]");
196 return 2;
197 }
Erik Andersen13456d12000-03-16 08:09:57 +0000198 argv[argc] = NULL;
199 }
Paul Fox6ab03782006-06-08 21:37:26 +0000200
201 res = setjmp(leaving);
202 if (res)
203 return res;
204
205 /* resetting ngroups is probably unnecessary. it will
206 * force a new call to getgroups(), which prevents using
207 * group data fetched during a previous call. but the
208 * only way the group data could be stale is if there's
209 * been an intervening call to setgroups(), and this
210 * isn't likely in the case of a shell. paranoia
211 * prevails...
212 */
213 ngroups = 0;
214
Erik Andersen13456d12000-03-16 08:09:57 +0000215 /* Implement special cases from POSIX.2, section 4.62.4 */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000216 if (argc == 1)
Paul Fox6ab03782006-06-08 21:37:26 +0000217 return 1;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000218 if (argc == 2)
Paul Fox6ab03782006-06-08 21:37:26 +0000219 return *argv[1] == '\0';
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000220//assert(argc);
221 if (LONE_CHAR(argv[1], '!')) {
222 bool _off;
223 if (argc == 3)
Paul Fox6ab03782006-06-08 21:37:26 +0000224 return *argv[2] != '\0';
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000225 _off = argc - 4;
226 if (t_lex(argv[2+_off]), t_wp_op && t_wp_op->op_type == BINOP) {
227 t_wp = &argv[1+_off];
228 return binop() == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000229 }
Erik Andersen13456d12000-03-16 08:09:57 +0000230 }
Erik Andersen13456d12000-03-16 08:09:57 +0000231 t_wp = &argv[1];
232 res = !oexpr(t_lex(*t_wp));
233
Paul Fox6ab03782006-06-08 21:37:26 +0000234 if (*t_wp != NULL && *++t_wp != NULL) {
235 bb_error_msg("%s: unknown operand", *t_wp);
236 return 2;
237 }
238 return res;
239}
Erik Andersen13456d12000-03-16 08:09:57 +0000240
Paul Fox6ab03782006-06-08 21:37:26 +0000241static void syntax(const char *op, const char *msg)
242{
243 if (op && *op) {
244 bb_error_msg("%s: %s", op, msg);
245 } else {
246 bb_error_msg("%s", msg);
247 }
248 longjmp(leaving, 2);
Erik Andersen13456d12000-03-16 08:09:57 +0000249}
250
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000251static arith_t oexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000252{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000253 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000254
255 res = aexpr(n);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000256 if (t_lex(*++t_wp) == BOR) {
Erik Andersen13456d12000-03-16 08:09:57 +0000257 return oexpr(t_lex(*++t_wp)) || res;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000258 }
Erik Andersen13456d12000-03-16 08:09:57 +0000259 t_wp--;
260 return res;
261}
262
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000263static arith_t aexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000264{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000265 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000266
267 res = nexpr(n);
268 if (t_lex(*++t_wp) == BAND)
269 return aexpr(t_lex(*++t_wp)) && res;
270 t_wp--;
271 return res;
272}
273
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000274static arith_t nexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000275{
276 if (n == UNOT)
277 return !nexpr(t_lex(*++t_wp));
278 return primary(n);
279}
280
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000281static arith_t primary(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000282{
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000283 arith_t res;
Erik Andersen13456d12000-03-16 08:09:57 +0000284
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000285 if (n == EOI) {
Paul Fox6ab03782006-06-08 21:37:26 +0000286 syntax(NULL, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000287 }
Erik Andersen13456d12000-03-16 08:09:57 +0000288 if (n == LPAREN) {
289 res = oexpr(t_lex(*++t_wp));
290 if (t_lex(*++t_wp) != RPAREN)
Paul Fox6ab03782006-06-08 21:37:26 +0000291 syntax(NULL, "closing paren expected");
Erik Andersen13456d12000-03-16 08:09:57 +0000292 return res;
293 }
294 if (t_wp_op && t_wp_op->op_type == UNOP) {
295 /* unary expression */
296 if (*++t_wp == NULL)
Paul Fox6ab03782006-06-08 21:37:26 +0000297 syntax(t_wp_op->op_text, "argument expected");
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000298 if (n == STREZ)
Erik Andersen13456d12000-03-16 08:09:57 +0000299 return strlen(*t_wp) == 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000300 else if (n == STRNZ)
Erik Andersen13456d12000-03-16 08:09:57 +0000301 return strlen(*t_wp) != 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000302 else if (n == FILTT)
Erik Andersen13456d12000-03-16 08:09:57 +0000303 return isatty(getn(*t_wp));
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000304 else
Erik Andersen13456d12000-03-16 08:09:57 +0000305 return filstat(*t_wp, n);
Erik Andersen13456d12000-03-16 08:09:57 +0000306 }
307
308 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
309 return binop();
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000310 }
Erik Andersen13456d12000-03-16 08:09:57 +0000311
312 return strlen(*t_wp) > 0;
313}
314
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000315static int binop(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000316{
317 const char *opnd1, *opnd2;
318 struct t_op const *op;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000319 smallint val1, val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000320
321 opnd1 = *t_wp;
322 (void) t_lex(*++t_wp);
323 op = t_wp_op;
324
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000325 if ((opnd2 = *++t_wp) == (char *) 0)
Paul Fox6ab03782006-06-08 21:37:26 +0000326 syntax(op->op_text, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000327
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000328 if (__is_int_op(op->op_num)) {
329 val1 = getn(opnd1);
330 val2 = getn(opnd2);
331 if (op->op_num == INTEQ)
332 return val1 == val2;
333 if (op->op_num == INTNE)
334 return val1 != val2;
335 if (op->op_num == INTGE)
336 return val1 >= val2;
337 if (op->op_num == INTGT)
338 return val1 > val2;
339 if (op->op_num == INTLE)
340 return val1 <= val2;
341 if (op->op_num == INTLT)
342 return val1 < val2;
Erik Andersen13456d12000-03-16 08:09:57 +0000343 }
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000344 if (__is_str_op(op->op_num)) {
345 val1 = strcmp(opnd1, opnd2);
346 if (op->op_num == STREQ)
347 return val1 == 0;
348 if (op->op_num == STRNE)
349 return val1 != 0;
350 if (op->op_num == STRLT)
351 return val1 < 0;
352 if (op->op_num == STRGT)
353 return val1 > 0;
354 }
355 /* We are sure that these three are by now the only binops we didn't check
356 * yet, so we do not check if the class is correct:
357 */
358/* if (__is_file_op(op->op_num)) */
359 {
360 struct stat b1, b2;
361
362 if (!(!stat(opnd1, &b1) && !stat(opnd2, &b2)))
363 return 0; /* false, since stat failed */
364 if (op->op_num == FILNT)
365 return b1.st_mtime > b2.st_mtime;
366 if (op->op_num == FILOT)
367 return b1.st_mtime < b2.st_mtime;
368 if (op->op_num == FILEQ)
369 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
370 }
371 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000372}
373
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000374static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000375{
376 struct stat s;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000377 int i;
Erik Andersen13456d12000-03-16 08:09:57 +0000378
379 if (mode == FILSYM) {
380#ifdef S_IFLNK
381 if (lstat(nm, &s) == 0) {
382 i = S_IFLNK;
383 goto filetype;
384 }
385#endif
386 return 0;
387 }
388
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000389 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000390 return 0;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000391 if (mode == FILEXIST)
Erik Andersen13456d12000-03-16 08:09:57 +0000392 return 1;
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000393 else if (__is_file_access(mode)) {
394 if (mode == FILRD)
395 i = R_OK;
396 if (mode == FILWR)
397 i = W_OK;
398 if (mode == FILEX)
399 i = X_OK;
400 return test_eaccess(nm, i) == 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000401 }
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000402 else if (__is_file_type(mode)) {
403 if (mode == FILREG)
404 i = S_IFREG;
405 if (mode == FILDIR)
406 i = S_IFDIR;
407 if (mode == FILCDEV)
408 i = S_IFCHR;
409 if (mode == FILBDEV)
410 i = S_IFBLK;
411 if (mode == FILFIFO) {
412#ifdef S_IFIFO
413 i = S_IFIFO;
414#else
415 return 0;
416#endif
417 }
418 if (mode == FILSOCK) {
419#ifdef S_IFSOCK
420 i = S_IFSOCK;
421#else
422 return 0;
423#endif
424 }
425filetype:
426 return ((s.st_mode & S_IFMT) == i);
427 }
428 else if (__is_file_bit(mode)) {
429 if (mode == FILSUID)
430 i = S_ISUID;
431 if (mode == FILSGID)
432 i = S_ISGID;
433 if (mode == FILSTCK)
434 i = S_ISVTX;
435 return ((s.st_mode & i) != 0);
436 }
437 else if (mode == FILGZ)
438 return s.st_size > 0L;
439 else if (mode == FILUID)
440 return s.st_uid == geteuid();
441 else if (mode == FILGID)
442 return s.st_gid == getegid();
443 else
444 return 1; /* NOTREACHED */
Erik Andersen13456d12000-03-16 08:09:57 +0000445
Erik Andersen13456d12000-03-16 08:09:57 +0000446}
447
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000448static enum token t_lex(char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000449{
450 struct t_op const *op = ops;
451
452 if (s == 0) {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000453 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000454 return EOI;
455 }
456 while (op->op_text) {
457 if (strcmp(s, op->op_text) == 0) {
458 t_wp_op = op;
459 return op->op_num;
460 }
461 op++;
462 }
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000463 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000464 return OPERAND;
465}
466
467/* atoi with error detection */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000468//XXX: FIXME: duplicate of existing libbb function?
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000469static arith_t getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000470{
471 char *p;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000472#ifdef CONFIG_FEATURE_TEST_64
473 long long r;
474#else
Erik Andersen13456d12000-03-16 08:09:57 +0000475 long r;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000476#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000477
478 errno = 0;
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000479#ifdef CONFIG_FEATURE_TEST_64
480 r = strtoll(s, &p, 10);
481#else
Erik Andersen13456d12000-03-16 08:09:57 +0000482 r = strtol(s, &p, 10);
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000483#endif
Erik Andersen13456d12000-03-16 08:09:57 +0000484
485 if (errno != 0)
Paul Fox6ab03782006-06-08 21:37:26 +0000486 syntax(s, "out of range");
Erik Andersen13456d12000-03-16 08:09:57 +0000487
Rob Landleyea224be2006-06-18 20:20:07 +0000488 if (*(skip_whitespace(p)))
Paul Fox6ab03782006-06-08 21:37:26 +0000489 syntax(s, "bad number");
Erik Andersen13456d12000-03-16 08:09:57 +0000490
Glenn L McGrath73db8be2004-08-11 02:45:47 +0000491 return r;
Erik Andersen13456d12000-03-16 08:09:57 +0000492}
493
Denis Vlasenko729bd9e2007-04-02 12:37:28 +0000494/* UNUSED
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000495static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000496{
497 struct stat b1, b2;
498
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000499 return (stat(f1, &b1) == 0 &&
500 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000501}
502
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000503static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000504{
505 struct stat b1, b2;
506
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000507 return (stat(f1, &b1) == 0 &&
508 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000509}
510
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000511static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000512{
513 struct stat b1, b2;
514
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000515 return (stat(f1, &b1) == 0 &&
516 stat(f2, &b2) == 0 &&
517 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000518}
Denis Vlasenko729bd9e2007-04-02 12:37:28 +0000519*/
Erik Andersen13456d12000-03-16 08:09:57 +0000520
521/* Do the same thing access(2) does, but use the effective uid and gid,
522 and don't make the mistake of telling root that any file is
523 executable. */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000524static int test_eaccess(char *path, int mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000525{
526 struct stat st;
Eric Andersenfad04fd2000-07-14 06:49:52 +0000527 unsigned int euid = geteuid();
Erik Andersen13456d12000-03-16 08:09:57 +0000528
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000529 if (stat(path, &st) < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000530 return -1;
Erik Andersen13456d12000-03-16 08:09:57 +0000531
532 if (euid == 0) {
533 /* Root can read or write any file. */
534 if (mode != X_OK)
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000535 return 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000536
537 /* Root can execute any file that has any one of the execute
538 bits set. */
539 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000540 return 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000541 }
542
Paul Fox6ab03782006-06-08 21:37:26 +0000543 if (st.st_uid == euid) /* owner */
Erik Andersen13456d12000-03-16 08:09:57 +0000544 mode <<= 6;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000545 else if (is_a_group_member(st.st_gid))
Erik Andersen13456d12000-03-16 08:09:57 +0000546 mode <<= 3;
547
548 if (st.st_mode & mode)
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000549 return 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000550
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000551 return -1;
Erik Andersen13456d12000-03-16 08:09:57 +0000552}
553
Mike Frysinger4e5936e2005-04-16 04:30:38 +0000554static void initialize_group_array(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000555{
556 ngroups = getgroups(0, NULL);
Rob Landleyecb29572006-08-22 23:40:28 +0000557 if (ngroups > 0) {
558 group_array = xmalloc(ngroups * sizeof(gid_t));
559 getgroups(ngroups, group_array);
560 }
Erik Andersen13456d12000-03-16 08:09:57 +0000561}
562
563/* Return non-zero if GID is one that we have in our groups list. */
Bernhard Reutner-Fischercc890262007-03-30 18:23:36 +0000564//XXX: FIXME: duplicate of existing libbb function?
565// see toplevel TODO file:
566// possible code duplication ingroup() and is_a_group_member()
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000567static int is_a_group_member(gid_t gid)
Erik Andersen13456d12000-03-16 08:09:57 +0000568{
"Robert P. J. Day"68229832006-07-01 13:08:46 +0000569 int i;
Erik Andersen13456d12000-03-16 08:09:57 +0000570
571 /* Short-circuit if possible, maybe saving a call to getgroups(). */
572 if (gid == getgid() || gid == getegid())
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000573 return 1;
Erik Andersen13456d12000-03-16 08:09:57 +0000574
575 if (ngroups == 0)
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000576 initialize_group_array();
Erik Andersen13456d12000-03-16 08:09:57 +0000577
578 /* Search through the list looking for GID. */
579 for (i = 0; i < ngroups; i++)
580 if (gid == group_array[i])
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000581 return 1;
Erik Andersen13456d12000-03-16 08:09:57 +0000582
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000583 return 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000584}
Paul Fox6ab03782006-06-08 21:37:26 +0000585
586
587/* applet entry point */
588
Denis Vlasenko06af2162007-02-03 17:28:39 +0000589int test_main(int argc, char **argv);
Paul Fox6ab03782006-06-08 21:37:26 +0000590int test_main(int argc, char **argv)
591{
Denis Vlasenkobf66fbc2006-12-21 13:23:14 +0000592 return bb_test(argc, argv);
Paul Fox6ab03782006-06-08 21:37:26 +0000593}
594