| [359] | 1 |  | 
|---|
 | 2 | /* | 
|---|
 | 3 |  * cronload.real.c | 
|---|
 | 4 |  * | 
|---|
 | 5 |  * CRONTAB | 
|---|
 | 6 |  * | 
|---|
 | 7 |  * usually setuid root, -c option only works if getuid() == geteuid() | 
|---|
 | 8 |  * | 
|---|
 | 9 |  * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com) | 
|---|
 | 10 |  * May be distributed under the GNU General Public License | 
|---|
 | 11 |  */ | 
|---|
 | 12 |  | 
|---|
 | 13 | #include "defs.h" | 
|---|
 | 14 |  | 
|---|
 | 15 | #define VERSION "$Revision: 359 $" | 
|---|
 | 16 |  | 
|---|
 | 17 | const char *CDir = SCRIPTS_CRONTABS; | 
|---|
 | 18 | int   UserId; | 
|---|
 | 19 | short LogLevel = 9; | 
|---|
 | 20 |  | 
|---|
 | 21 | int GetReplaceStream(const char *user, const char *file); | 
|---|
 | 22 | extern int ChangeUser(const char *user, short dochdir); | 
|---|
 | 23 |  | 
|---|
 | 24 | int | 
|---|
 | 25 | main(int ac, char **av) | 
|---|
 | 26 | { | 
|---|
 | 27 |     enum { NONE, LIST, REPLACE, DELETE } option = NONE; | 
|---|
 | 28 |     struct passwd *pas; | 
|---|
 | 29 |     char *repFile = NULL; | 
|---|
 | 30 |     int repFd = 0; | 
|---|
 | 31 |     int i; | 
|---|
 | 32 |     char caller[256];           /* user that ran program */ | 
|---|
 | 33 |  | 
|---|
 | 34 |     UserId = getuid(); | 
|---|
 | 35 |     if ((pas = getpwuid(UserId)) == NULL) { | 
|---|
 | 36 |         perror("getpwuid"); | 
|---|
 | 37 |         exit(1); | 
|---|
 | 38 |     } | 
|---|
 | 39 |     snprintf(caller, sizeof(caller), "%s", pas->pw_name); | 
|---|
 | 40 |  | 
|---|
 | 41 |     i = 1; | 
|---|
 | 42 |     if (ac > 1) { | 
|---|
 | 43 |         if (av[1][0] == '-' && av[1][1] == 0) { | 
|---|
 | 44 |             option = REPLACE; | 
|---|
 | 45 |             ++i; | 
|---|
 | 46 |         } else if (av[1][0] != '-') { | 
|---|
 | 47 |             option = REPLACE; | 
|---|
 | 48 |             ++i; | 
|---|
 | 49 |             repFile = av[1]; | 
|---|
 | 50 |         } | 
|---|
 | 51 |     } | 
|---|
 | 52 |  | 
|---|
 | 53 |     for (; i < ac; ++i) { | 
|---|
 | 54 |         char *ptr = av[i]; | 
|---|
 | 55 |  | 
|---|
 | 56 |         if (*ptr != '-') | 
|---|
 | 57 |             break; | 
|---|
 | 58 |         ptr += 2; | 
|---|
 | 59 |  | 
|---|
 | 60 |         switch(ptr[-1]) { | 
|---|
 | 61 |         case 'l': | 
|---|
 | 62 |             if (ptr[-1] == 'l') | 
|---|
 | 63 |                 option = LIST; | 
|---|
 | 64 |             /* fall through */ | 
|---|
 | 65 |         case 'd': | 
|---|
 | 66 |             if (ptr[-1] == 'd') | 
|---|
 | 67 |                 option = DELETE; | 
|---|
 | 68 |             /* fall through */ | 
|---|
 | 69 |         case 'u': | 
|---|
 | 70 |             if (i + 1 < ac && av[i+1][0] != '-') { | 
|---|
 | 71 |                 ++i; | 
|---|
 | 72 |                 if (getuid() == geteuid()) { | 
|---|
 | 73 |                     pas = getpwnam(av[i]); | 
|---|
 | 74 |                     if (pas) { | 
|---|
 | 75 |                         UserId = pas->pw_uid; | 
|---|
 | 76 |                     } else { | 
|---|
 | 77 |                         errx(1, "user %s unknown\n", av[i]); | 
|---|
 | 78 |                     } | 
|---|
 | 79 |                 } else { | 
|---|
 | 80 |                     errx(1, "only the superuser may specify a user\n"); | 
|---|
 | 81 |                 } | 
|---|
 | 82 |             } | 
|---|
 | 83 |             break; | 
|---|
 | 84 |         case 'c': | 
|---|
 | 85 |             if ((getuid() == geteuid()) && (0 == getuid())) { | 
|---|
 | 86 |                 CDir = (*ptr) ? ptr : av[++i]; | 
|---|
 | 87 |             } else { | 
|---|
 | 88 |                 errx(1, "-c option: superuser only\n"); | 
|---|
 | 89 |             } | 
|---|
 | 90 |             break; | 
|---|
 | 91 |         default: | 
|---|
 | 92 |             i = ac; | 
|---|
 | 93 |             break; | 
|---|
 | 94 |         } | 
|---|
 | 95 |     } | 
|---|
 | 96 |     if (i != ac || option == NONE) { | 
|---|
 | 97 |         printf("cronload.real " VERSION "\n"); | 
|---|
 | 98 |         printf("cronload.real file <opts>  replace crontab from file\n"); | 
|---|
 | 99 |         printf("cronload.real -    <opts>  replace crontab from stdin\n"); | 
|---|
 | 100 |         printf("cronload.real -u user      specify user\n"); | 
|---|
 | 101 |         printf("cronload.real -l [user]    list crontab for user\n"); | 
|---|
 | 102 |         printf("cronload.real -d [user]    delete crontab for user\n"); | 
|---|
 | 103 |         printf("cronload.real -c dir       specify crontab directory\n"); | 
|---|
 | 104 |         exit(0); | 
|---|
 | 105 |     } | 
|---|
 | 106 |  | 
|---|
 | 107 |     /* | 
|---|
 | 108 |      * Get password entry | 
|---|
 | 109 |      */ | 
|---|
 | 110 |  | 
|---|
 | 111 |     if ((pas = getpwuid(UserId)) == NULL) { | 
|---|
 | 112 |         perror("getpwuid"); | 
|---|
 | 113 |         exit(1); | 
|---|
 | 114 |     } | 
|---|
 | 115 |  | 
|---|
 | 116 |     /* | 
|---|
 | 117 |      * If there is a replacement file, obtain a secure descriptor to it. | 
|---|
 | 118 |      */ | 
|---|
 | 119 |  | 
|---|
 | 120 |     if (repFile) { | 
|---|
 | 121 |         repFd = GetReplaceStream(caller, repFile); | 
|---|
 | 122 |         if (repFd < 0) { | 
|---|
 | 123 |             errx(1, "unable to read replacement file\n"); | 
|---|
 | 124 |         } | 
|---|
 | 125 |     } | 
|---|
 | 126 |  | 
|---|
 | 127 |     /* | 
|---|
 | 128 |      * Change directory to our crontab directory | 
|---|
 | 129 |      */ | 
|---|
 | 130 |  | 
|---|
 | 131 |     if (chdir(CDir) < 0) { | 
|---|
 | 132 |         errx(1, "cannot change dir to %s: %s\n", CDir, strerror(errno)); | 
|---|
 | 133 |     } | 
|---|
 | 134 |  | 
|---|
 | 135 |     /* | 
|---|
 | 136 |      * Handle options as appropriate | 
|---|
 | 137 |      */ | 
|---|
 | 138 |  | 
|---|
 | 139 |     switch(option) { | 
|---|
 | 140 |     case LIST: | 
|---|
 | 141 |         { | 
|---|
 | 142 |             FILE *fi; | 
|---|
 | 143 |             char buf[1024]; | 
|---|
 | 144 |  | 
|---|
 | 145 |             if ((fi = fopen(pas->pw_name, "r"))) { | 
|---|
 | 146 |                 while (fgets(buf, sizeof(buf), fi) != NULL) | 
|---|
 | 147 |                     fputs(buf, stdout); | 
|---|
 | 148 |                 fclose(fi); | 
|---|
 | 149 |             } else { | 
|---|
 | 150 |                 fprintf(stderr, "no crontab for %s\n", pas->pw_name); | 
|---|
 | 151 |             } | 
|---|
 | 152 |         } | 
|---|
 | 153 |         break; | 
|---|
 | 154 |     case REPLACE: | 
|---|
 | 155 |         { | 
|---|
 | 156 |             char buf[1024]; | 
|---|
 | 157 |             char path[1024]; | 
|---|
 | 158 |             int fd; | 
|---|
 | 159 |             int n; | 
|---|
 | 160 |  | 
|---|
 | 161 |             snprintf(path, sizeof(path), "%s.new", pas->pw_name); | 
|---|
 | 162 |             if ((fd = open(path, O_CREAT|O_TRUNC|O_EXCL|O_APPEND|O_WRONLY, 0600)) >= 0) { | 
|---|
 | 163 |                 while ((n = read(repFd, buf, sizeof(buf))) > 0) { | 
|---|
 | 164 |                     write(fd, buf, n); | 
|---|
 | 165 |                 } | 
|---|
 | 166 |                 close(fd); | 
|---|
 | 167 |                 rename(path, pas->pw_name); | 
|---|
 | 168 |             } else { | 
|---|
 | 169 |                 fprintf(stderr, "unable to create %s/%s: %s\n",  | 
|---|
 | 170 |                     CDir, | 
|---|
 | 171 |                     path, | 
|---|
 | 172 |                     strerror(errno) | 
|---|
 | 173 |                 ); | 
|---|
 | 174 |             } | 
|---|
 | 175 |             close(repFd); | 
|---|
 | 176 |         } | 
|---|
 | 177 |         break; | 
|---|
 | 178 |     case DELETE: | 
|---|
 | 179 |         remove(pas->pw_name); | 
|---|
 | 180 |         break; | 
|---|
 | 181 |     case NONE: | 
|---|
 | 182 |     default:  | 
|---|
 | 183 |         break; | 
|---|
 | 184 |     } | 
|---|
 | 185 |  | 
|---|
 | 186 |     /* | 
|---|
 | 187 |      *  Bump notification file.  Handle window where crond picks file up | 
|---|
 | 188 |      *  before we can write our entry out. | 
|---|
 | 189 |      */ | 
|---|
 | 190 |         /* // only applicable to dcron | 
|---|
 | 191 |     if (option == REPLACE || option == DELETE) { | 
|---|
 | 192 |         FILE *fo; | 
|---|
 | 193 |         struct stat st; | 
|---|
 | 194 |  | 
|---|
 | 195 |         while ((fo = fopen(CRONUPDATE, "a"))) { | 
|---|
 | 196 |                         fprintf(fo, "%s\n", pas->pw_name); | 
|---|
 | 197 |                         fflush(fo); | 
|---|
 | 198 |                         if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) { | 
|---|
 | 199 |                         fclose(fo); | 
|---|
 | 200 |                         break; | 
|---|
 | 201 |                         } | 
|---|
 | 202 |                         fclose(fo); | 
|---|
 | 203 |                         // * loop * / | 
|---|
 | 204 |                 } | 
|---|
 | 205 |                 if (fo == NULL) { | 
|---|
 | 206 |                         fprintf(stderr, "unable to append to %s/%s\n", CDir, CRONUPDATE); | 
|---|
 | 207 |                 } | 
|---|
 | 208 |     } | 
|---|
 | 209 |     */ | 
|---|
 | 210 |     (volatile void)exit(0); | 
|---|
 | 211 |     /* not reached */ | 
|---|
 | 212 | } | 
|---|
 | 213 |  | 
|---|
 | 214 | int | 
|---|
 | 215 | GetReplaceStream(const char *user, const char *file) | 
|---|
 | 216 | { | 
|---|
 | 217 |     int filedes[2]; | 
|---|
 | 218 |     int pid; | 
|---|
 | 219 |     int fd; | 
|---|
 | 220 |     int n; | 
|---|
 | 221 |     char buf[1024]; | 
|---|
 | 222 |  | 
|---|
 | 223 |     if (pipe(filedes) < 0) { | 
|---|
 | 224 |         perror("pipe"); | 
|---|
 | 225 |         return(-1); | 
|---|
 | 226 |     } | 
|---|
 | 227 |     if ((pid = fork()) < 0) { | 
|---|
 | 228 |         perror("fork"); | 
|---|
 | 229 |         return(-1); | 
|---|
 | 230 |     } | 
|---|
 | 231 |     if (pid > 0) { | 
|---|
 | 232 |         /* | 
|---|
 | 233 |          * PARENT | 
|---|
 | 234 |          */ | 
|---|
 | 235 |  | 
|---|
 | 236 |         close(filedes[1]); | 
|---|
 | 237 |         if (read(filedes[0], buf, 1) != 1) { | 
|---|
 | 238 |             close(filedes[0]); | 
|---|
 | 239 |             filedes[0] = -1; | 
|---|
 | 240 |         } | 
|---|
 | 241 |         return(filedes[0]); | 
|---|
 | 242 |     } | 
|---|
 | 243 |  | 
|---|
 | 244 |     /* | 
|---|
 | 245 |      * CHILD | 
|---|
 | 246 |      */ | 
|---|
 | 247 |  | 
|---|
 | 248 |     close(filedes[0]); | 
|---|
 | 249 |  | 
|---|
 | 250 |     if (ChangeUser(user, 0) < 0) | 
|---|
 | 251 |         exit(0); | 
|---|
 | 252 |  | 
|---|
 | 253 |     fd = open(file, O_RDONLY); | 
|---|
 | 254 |     if (fd < 0) | 
|---|
 | 255 |         errx(0, "unable to open %s\n", file); | 
|---|
 | 256 |     buf[0] = 0; | 
|---|
 | 257 |     write(filedes[1], buf, 1); | 
|---|
 | 258 |     while ((n = read(fd, buf, sizeof(buf))) > 0) { | 
|---|
 | 259 |         write(filedes[1], buf, n); | 
|---|
 | 260 |     } | 
|---|
 | 261 |     exit(0); | 
|---|
 | 262 | } | 
|---|