| 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 | } | 
|---|