Made ps work. Fixed some stuff.
[oweals/busybox.git] / utility.c
1 /*
2  * Utility routines.
3  *
4  * Copyright (C) 1998 by Erik Andersen <andersee@debian.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell 
21  * Permission has been granted to redistribute this code under the GPL.
22  *
23  */
24
25 #include "internal.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <time.h>
32 #include <utime.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <ctype.h>
36
37 /* volatile so gcc knows this is the enod of the line */
38 volatile void usage(const char *usage)
39 {
40     fprintf(stderr, "Usage: %s\n", usage);
41     exit(FALSE);
42 }
43
44
45 #if defined (BB_INIT) || defined (BB_PS)
46
47 /* Returns kernel version encoded as major*65536 + minor*256 + patch,
48  * so, for example,  to check if the kernel is greater than 2.2.11:
49  *      if (get_kernel_revision() <= 2*65536+2*256+11) { <stuff> }
50  */
51 int
52 get_kernel_revision()
53 {
54   FILE *f;
55   int major=0, minor=0, patch=0;
56
57   f = fopen("/proc/sys/kernel/osrelease","r");
58   fscanf(f,"%d.%d.%d",&major,&minor,&patch);
59   fclose(f);
60   return major*65536 + minor*256 + patch;
61 }
62
63 #endif
64
65
66
67 #if defined (BB_CP) || defined (BB_MV)
68 /*
69  * Return TRUE if a fileName is a directory.
70  * Nonexistant files return FALSE.
71  */
72 int isDirectory(const char *name)
73 {
74     struct stat statBuf;
75
76     if (stat(name, &statBuf) < 0)
77         return FALSE;
78     if (S_ISDIR(statBuf.st_mode))
79         return TRUE;
80     return(FALSE);
81 }
82
83
84 /*
85  * Copy one file to another, while possibly preserving its modes, times,
86  * and modes.  Returns TRUE if successful, or FALSE on a failure with an
87  * error message output.  (Failure is not indicted if the attributes cannot
88  * be set.)
89  */
90 int
91 copyFile( const char *srcName, const char *destName, 
92          int setModes, int followLinks)
93 {
94     int rfd;
95     int wfd;
96     int rcc;
97     int result;
98     char buf[BUF_SIZE];
99     struct stat srcStatBuf;
100     struct stat dstStatBuf;
101     struct utimbuf times;
102
103     if (followLinks == FALSE)
104         result = stat(srcName, &srcStatBuf);
105     else 
106         result = lstat(srcName, &srcStatBuf);
107     if (result < 0) {
108         perror(srcName);
109         return FALSE;
110     }
111
112     if (followLinks == FALSE)
113         result = stat(destName, &dstStatBuf);
114     else 
115         result = lstat(destName, &dstStatBuf);
116     if (result < 0) {
117         dstStatBuf.st_ino = -1;
118         dstStatBuf.st_dev = -1;
119     }
120
121     if ((srcStatBuf.st_dev == dstStatBuf.st_dev) &&
122         (srcStatBuf.st_ino == dstStatBuf.st_ino)) {
123         fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);
124         return FALSE;
125     }
126
127     if (S_ISDIR(srcStatBuf.st_mode)) {
128         //fprintf(stderr, "copying directory %s to %s\n", srcName, destName);
129         /* Make sure the directory is writable */
130         if (mkdir(destName, 0777777 ^ umask(0))) {
131             perror(destName);
132             return (FALSE);
133         }
134     } else if (S_ISLNK(srcStatBuf.st_mode)) {
135         char *link_val;
136         int link_size;
137
138         //fprintf(stderr, "copying link %s to %s\n", srcName, destName);
139         link_val = (char *) alloca(PATH_MAX + 2);
140         link_size = readlink(srcName, link_val, PATH_MAX + 1);
141         if (link_size < 0) {
142             perror(srcName);
143             return (FALSE);
144         }
145         link_val[link_size] = '\0';
146         link_size = symlink(link_val, destName);
147         if (link_size != 0) {
148             perror(destName);
149             return (FALSE);
150         }
151     } else if (S_ISFIFO(srcStatBuf.st_mode)) {
152         //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
153         if (mkfifo(destName, 644)) {
154             perror(destName);
155             return (FALSE);
156         }
157     } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) 
158             || S_ISSOCK (srcStatBuf.st_mode)) {
159         //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName);
160         if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev)) {
161             perror(destName);
162             return (FALSE);
163         }
164     } else if (S_ISREG(srcStatBuf.st_mode)) {
165         //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName);
166         rfd = open(srcName, O_RDONLY);
167         if (rfd < 0) {
168             perror(srcName);
169             return FALSE;
170         }
171
172         wfd = creat(destName, srcStatBuf.st_mode);
173         if (wfd < 0) {
174             perror(destName);
175             close(rfd);
176             return FALSE;
177         }
178
179         while ((rcc = read(rfd, buf, sizeof(buf))) > 0) {
180             if (fullWrite(wfd, buf, rcc) < 0)
181                 goto error_exit;
182         }
183         if (rcc < 0) {
184             goto error_exit;
185         }
186
187         close(rfd);
188         if (close(wfd) < 0) {
189             return FALSE;
190         }
191     }
192
193     if (setModes == TRUE) {
194         //fprintf(stderr, "Setting permissions for %s\n", destName);
195         chmod(destName, srcStatBuf.st_mode);
196         if (followLinks == TRUE)
197             chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
198         else
199             lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
200
201         times.actime = srcStatBuf.st_atime;
202         times.modtime = srcStatBuf.st_mtime;
203
204         utime(destName, &times);
205     }
206
207     return TRUE;
208
209
210   error_exit:
211     perror(destName);
212     close(rfd);
213     close(wfd);
214
215     return FALSE;
216 }
217 #endif
218
219
220
221 #ifdef BB_TAR
222 /*
223  * Return the standard ls-like mode string from a file mode.
224  * This is static and so is overwritten on each call.
225  */
226 const char *modeString(int mode)
227 {
228     static char buf[12];
229
230     strcpy(buf, "----------");
231
232     /*
233      * Fill in the file type.
234      */
235     if (S_ISDIR(mode))
236         buf[0] = 'd';
237     if (S_ISCHR(mode))
238         buf[0] = 'c';
239     if (S_ISBLK(mode))
240         buf[0] = 'b';
241     if (S_ISFIFO(mode))
242         buf[0] = 'p';
243     if (S_ISLNK(mode))
244         buf[0] = 'l';
245     if (S_ISSOCK(mode))
246         buf[0] = 's';
247     /*
248      * Now fill in the normal file permissions.
249      */
250     if (mode & S_IRUSR)
251         buf[1] = 'r';
252     if (mode & S_IWUSR)
253         buf[2] = 'w';
254     if (mode & S_IXUSR)
255         buf[3] = 'x';
256     if (mode & S_IRGRP)
257         buf[4] = 'r';
258     if (mode & S_IWGRP)
259         buf[5] = 'w';
260     if (mode & S_IXGRP)
261         buf[6] = 'x';
262     if (mode & S_IROTH)
263         buf[7] = 'r';
264     if (mode & S_IWOTH)
265         buf[8] = 'w';
266     if (mode & S_IXOTH)
267         buf[9] = 'x';
268
269     /*
270      * Finally fill in magic stuff like suid and sticky text.
271      */
272     if (mode & S_ISUID)
273         buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
274     if (mode & S_ISGID)
275         buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
276     if (mode & S_ISVTX)
277         buf[9] = ((mode & S_IXOTH) ? 't' : 'T');
278
279     return buf;
280 }
281
282
283 /*
284  * Get the time string to be used for a file.
285  * This is down to the minute for new files, but only the date for old files.
286  * The string is returned from a static buffer, and so is overwritten for
287  * each call.
288  */
289 const char *timeString(time_t timeVal)
290 {
291     time_t now;
292     char *str;
293     static char buf[26];
294
295     time(&now);
296
297     str = ctime(&timeVal);
298
299     strcpy(buf, &str[4]);
300     buf[12] = '\0';
301
302     if ((timeVal > now) || (timeVal < now - 365 * 24 * 60 * 60L)) {
303         strcpy(&buf[7], &str[20]);
304         buf[11] = '\0';
305     }
306
307     return buf;
308 }
309
310
311 /*
312  * Routine to see if a text string is matched by a wildcard pattern.
313  * Returns TRUE if the text is matched, or FALSE if it is not matched
314  * or if the pattern is invalid.
315  *  *           matches zero or more characters
316  *  ?           matches a single character
317  *  [abc]       matches 'a', 'b' or 'c'
318  *  \c          quotes character c
319  *  Adapted from code written by Ingo Wilken.
320  */
321 int match(const char *text, const char *pattern)
322 {
323     const char *retryPat;
324     const char *retryText;
325     int ch;
326     int found;
327
328     retryPat = NULL;
329     retryText = NULL;
330
331     while (*text || *pattern) {
332         ch = *pattern++;
333
334         switch (ch) {
335         case '*':
336             retryPat = pattern;
337             retryText = text;
338             break;
339
340         case '[':
341             found = FALSE;
342
343             while ((ch = *pattern++) != ']') {
344                 if (ch == '\\')
345                     ch = *pattern++;
346
347                 if (ch == '\0')
348                     return FALSE;
349
350                 if (*text == ch)
351                     found = TRUE;
352             }
353
354             if (!found) {
355                 pattern = retryPat;
356                 text = ++retryText;
357             }
358
359             /* fall into next case */
360
361         case '?':
362             if (*text++ == '\0')
363                 return FALSE;
364
365             break;
366
367         case '\\':
368             ch = *pattern++;
369
370             if (ch == '\0')
371                 return FALSE;
372
373             /* fall into next case */
374
375         default:
376             if (*text == ch) {
377                 if (*text)
378                     text++;
379                 break;
380             }
381
382             if (*text) {
383                 pattern = retryPat;
384                 text = ++retryText;
385                 break;
386             }
387
388             return FALSE;
389         }
390
391         if (pattern == NULL)
392             return FALSE;
393     }
394
395     return TRUE;
396 }
397
398
399 /*
400  * Write all of the supplied buffer out to a file.
401  * This does multiple writes as necessary.
402  * Returns the amount written, or -1 on an error.
403  */
404 int fullWrite(int fd, const char *buf, int len)
405 {
406     int cc;
407     int total;
408
409     total = 0;
410
411     while (len > 0) {
412         cc = write(fd, buf, len);
413
414         if (cc < 0)
415             return -1;
416
417         buf += cc;
418         total += cc;
419         len -= cc;
420     }
421
422     return total;
423 }
424
425
426 /*
427  * Read all of the supplied buffer from a file.
428  * This does multiple reads as necessary.
429  * Returns the amount read, or -1 on an error.
430  * A short read is returned on an end of file.
431  */
432 int fullRead(int fd, char *buf, int len)
433 {
434     int cc;
435     int total;
436
437     total = 0;
438
439     while (len > 0) {
440         cc = read(fd, buf, len);
441
442         if (cc < 0)
443             return -1;
444
445         if (cc == 0)
446             break;
447
448         buf += cc;
449         total += cc;
450         len -= cc;
451     }
452
453     return total;
454 }
455 #endif
456
457
458 #if defined (BB_CHOWN) || defined (BB_CP) || defined (BB_FIND) || defined (BB_LS)
459 /*
460  * Walk down all the directories under the specified 
461  * location, and do something (something specified
462  * by the fileAction and dirAction function pointers).
463  *
464  * TODO: check if ftw(3) can replace this to reduce code size...
465  */
466 int
467 recursiveAction(const char *fileName, int recurse, int followLinks, int delayDirAction,
468                 int (*fileAction) (const char *fileName, struct stat* statbuf),
469                 int (*dirAction) (const char *fileName, struct stat* statbuf))
470 {
471     int status;
472     struct stat statbuf;
473     struct dirent *next;
474
475     if (followLinks == FALSE)
476         status = stat(fileName, &statbuf);
477     else
478         status = lstat(fileName, &statbuf);
479
480     if (status < 0) {
481         perror(fileName);
482         return (FALSE);
483     }
484
485     if (recurse == FALSE) {
486         if (S_ISDIR(statbuf.st_mode)) {
487             if (dirAction != NULL)
488                 return (dirAction(fileName, &statbuf));
489             else
490                 return (TRUE);
491         } 
492     }
493
494     if (S_ISDIR(statbuf.st_mode)) {
495         DIR *dir;
496         dir = opendir(fileName);
497         if (!dir) {
498             perror(fileName);
499             return (FALSE);
500         }
501         if (dirAction != NULL && delayDirAction == FALSE) {
502             status = dirAction(fileName, &statbuf);
503             if (status == FALSE) {
504                 perror(fileName);
505                 return (FALSE);
506             }
507         }
508         while ((next = readdir(dir)) != NULL) {
509             char nextFile[NAME_MAX];
510             if ((strcmp(next->d_name, "..") == 0)
511                 || (strcmp(next->d_name, ".") == 0)) {
512                 continue;
513             }
514             sprintf(nextFile, "%s/%s", fileName, next->d_name);
515             status =
516                 recursiveAction(nextFile, TRUE, followLinks, delayDirAction, 
517                         fileAction, dirAction);
518             if (status < 0) {
519                 closedir(dir);
520                 return (FALSE);
521             }
522         }
523         status = closedir(dir);
524         if (status < 0) {
525             perror(fileName);
526             return (FALSE);
527         }
528         if (dirAction != NULL && delayDirAction == TRUE) {
529             status = dirAction(fileName, &statbuf);
530             if (status == FALSE) {
531                 perror(fileName);
532                 return (FALSE);
533             }
534         }
535     } else {
536         if (fileAction == NULL)
537             return (TRUE);
538         else
539             return (fileAction(fileName, &statbuf));
540     }
541     return (TRUE);
542 }
543
544 #endif
545
546
547
548 #if defined (BB_TAR) || defined (BB_MKDIR)
549 /*
550  * Attempt to create the directories along the specified path, except for
551  * the final component.  The mode is given for the final directory only,
552  * while all previous ones get default protections.  Errors are not reported
553  * here, as failures to restore files can be reported later.
554  */
555 extern void createPath (const char *name, int mode)
556 {
557     char *cp;
558     char *cpOld;
559     char buf[NAME_MAX];
560
561     strcpy (buf, name);
562
563     cp = strchr (buf, '/');
564
565     while (cp) {
566         cpOld = cp;
567         cp = strchr (cp + 1, '/');
568
569         *cpOld = '\0';
570
571         if (mkdir (buf, cp ? 0777 : mode) == 0)
572             printf ("Directory \"%s\" created\n", buf);
573
574         *cpOld = '/';
575     }
576 }
577 #endif
578
579
580
581 #if defined (BB_CHMOD_CHOWN_CHGRP) || defined (BB_MKDIR)
582 /* [ugoa]{+|-|=}[rwxst] */
583
584
585
586 extern int 
587 parse_mode( const char* s, mode_t* theMode)
588 {
589         mode_t andMode = S_ISVTX|S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
590         mode_t orMode = 0;
591         mode_t  mode = 0;
592         mode_t  groups = 0;
593         char    type;
594         char    c;
595
596         do {
597                 for ( ; ; ) {
598                         switch ( c = *s++ ) {
599                         case '\0':
600                                 return -1;
601                         case 'u':
602                                 groups |= S_ISUID|S_IRWXU;
603                                 continue;
604                         case 'g':
605                                 groups |= S_ISGID|S_IRWXG;
606                                 continue;
607                         case 'o':
608                                 groups |= S_IRWXO;
609                                 continue;
610                         case 'a':
611                                 groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
612                                 continue;
613                         case '+':
614                         case '=':
615                         case '-':
616                                 type = c;
617                                 if ( groups == 0 ) /* The default is "all" */
618                                         groups |= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
619                                 break;
620                         default:
621                                 if ( isdigit(c) && c >= '0' && c <= '7' && mode == 0 && groups == 0 ) {
622                                         andMode = 0;
623                                         orMode = strtol(--s, NULL, 8);
624                                         *theMode &= andMode;
625                                         *theMode |= orMode;
626                                         return (TRUE);
627                                 }
628                                 else
629                                         return (FALSE);
630                         }
631                         break;
632                 }
633
634                 while ( (c = *s++) != '\0' ) {
635                         switch ( c ) {
636                         case ',':
637                                 break;
638                         case 'r':
639                                 mode |= S_IRUSR|S_IRGRP|S_IROTH;
640                                 continue;
641                         case 'w':
642                                 mode |= S_IWUSR|S_IWGRP|S_IWOTH;
643                                 continue;
644                         case 'x':
645                                 mode |= S_IXUSR|S_IXGRP|S_IXOTH;
646                                 continue;
647                         case 's':
648                                 mode |= S_IXGRP|S_ISUID|S_ISGID;
649                                 continue;
650                         case 't':
651                                 mode |= 0;
652                                 continue;
653                         default:
654                                 *theMode &= andMode;
655                                 *theMode |= orMode;
656                                 return( TRUE);
657                         }
658                         break;
659                 }
660                 switch ( type ) {
661                 case '=':
662                         andMode &= ~(groups);
663                         /* fall through */
664                 case '+':
665                         orMode |= mode & groups;
666                         break;
667                 case '-':
668                         andMode &= ~(mode & groups);
669                         orMode &= andMode;
670                         break;
671                 }
672         } while ( c == ',' );
673         *theMode &= andMode;
674         *theMode |= orMode;
675         return (TRUE);
676 }
677
678
679 #endif
680
681
682
683
684
685
686
687 #if defined (BB_CHMOD_CHOWN_CHGRP) || defined (BB_PS)
688
689 /* Use this to avoid needing the glibc NSS stuff 
690  * This uses storage buf to hold things.
691  * */
692 uid_t 
693 my_getid(const char *filename, char *name, uid_t id) 
694 {
695         FILE *stream;
696         char *rname, *start, *end, buf[128];
697         uid_t rid;
698
699         stream=fopen(filename,"r");
700
701         while (fgets (buf, 128, stream) != NULL) {
702                 if (buf[0] == '#')
703                         continue;
704
705                 start = buf;
706                 end = strchr (start, ':');
707                 if (end == NULL)
708                         continue;
709                 *end = '\0';
710                 rname = start;
711
712                 start = end + 1;
713                 end = strchr (start, ':');
714                 if (end == NULL)
715                         continue;
716
717                 start = end + 1;
718                 rid = (uid_t) strtol (start, &end, 10);
719                 if (end == start)
720                         continue;
721
722                 if (name) {
723                     if (0 == strcmp(rname, name))
724                         return( rid);
725                 }
726                 if ( id != -1 && id == rid ) {
727                     strncpy(name, rname, 8);
728                     return( TRUE);
729                 }
730         }
731         fclose(stream);
732         return (-1);
733 }
734
735 uid_t 
736 my_getpwnam(char *name) 
737 {
738     return my_getid("/etc/passwd", name, -1);
739 }
740
741 gid_t 
742 my_getgrnam(char *name) 
743 {
744     return my_getid("/etc/group", name, -1);
745 }
746
747 void
748 my_getpwuid(char* name, uid_t uid) 
749 {
750     my_getid("/etc/passwd", name, uid);
751 }
752
753 void
754 my_getgrgid(char* group, gid_t gid) 
755 {
756     my_getid("/etc/group", group, gid);
757 }
758
759
760 #endif
761
762
763 /* END CODE */