1 /*- 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1983, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)atq.c 8.1 (Berkeley) 06/06/93"; 16 #endif /* not lint */ 17 18 /* 19 * 20 * Synopsis: atq [ -c ] [ -n ] [ name ... ] 21 * 22 * 23 * Print the queue of files waiting to be executed. These files 24 * were created by using the "at" command and are located in the 25 * directory "/usr/spool/at". 26 * 27 * 28 * Author: Steve Wall 29 * Computer Systems Research Group 30 * University of California @ Berkeley 31 * 32 */ 33 34 # include <stdio.h> 35 # include <sys/types.h> 36 # include <sys/file.h> 37 # include <sys/dir.h> 38 # include <sys/stat.h> 39 # include <sys/time.h> 40 # include <pwd.h> 41 # include <ctype.h> 42 # include "pathnames.h" 43 44 /* 45 * Months of the year 46 */ 47 static char *mthnames[12] = { 48 "Jan","Feb","Mar","Apr","May","Jun","Jul", 49 "Aug","Sep","Oct","Nov","Dec", 50 }; 51 52 char *nullentry = NULL; /* avoid 'namelist' NULL ptr problems */ 53 int numentries; /* number of entries in spooling area */ 54 int namewanted = 0; /* only print jobs belonging to a 55 certain person */ 56 struct direct **queue; /* the queue itself */ 57 58 59 main(argc,argv) 60 int argc; 61 char **argv; 62 { 63 64 int cflag = 0; /* print in order of creation time */ 65 int nflag = 0; /* just print the number of jobs in 66 queue */ 67 int usage(); /* print usage info and exit */ 68 int creation(); /* sort jobs by date of creation */ 69 int alphasort(); /* sort jobs by date of execution */ 70 int filewanted(); /* should a file be included in queue?*/ 71 int printqueue(); /* print the queue */ 72 int countfiles(); /* count the number of files in queue 73 for a given person */ 74 char **namelist = &nullentry; /* array of specific name(s) requested*/ 75 76 77 --argc, ++argv; 78 79 /* 80 * Interpret command line flags if they exist. 81 */ 82 while (argc > 0 && **argv == '-') { 83 (*argv)++; 84 while (**argv) switch (*(*argv)++) { 85 86 case 'c' : cflag++; 87 break; 88 89 case 'n' : nflag++; 90 break; 91 92 default : usage(); 93 94 } 95 --argc, ++argv; 96 } 97 98 /* 99 * If a certain name (or names) is requested, set a pointer to the 100 * beginning of the list. 101 */ 102 if (argc > 0) { 103 ++namewanted; 104 namelist = argv; 105 } 106 107 /* 108 * Move to the spooling area and scan the directory, placing the 109 * files in the queue structure. The queue comes back sorted by 110 * execution time or creation time. 111 */ 112 if (chdir(_PATH_ATDIR) == -1) { 113 perror(_PATH_ATDIR); 114 exit(1); 115 } 116 if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 117 alphasort)) < 0) { 118 perror(_PATH_ATDIR); 119 exit(1); 120 } 121 122 /* 123 * Either print a message stating: 124 * 125 * 1) that the spooling area is empty. 126 * 2) the number of jobs in the spooling area. 127 * 3) the number of jobs in the spooling area belonging to 128 * a certain person. 129 * 4) that the person requested doesn't have any files in the 130 * spooling area. 131 * 132 * or send the queue off to "printqueue" for printing. 133 * 134 * This whole process might seem a bit elaborate, but it's worthwhile 135 * to print some informative messages for the user. 136 * 137 */ 138 if ((numentries == 0) && (!nflag)) { 139 printf("no files in queue.\n"); 140 exit(0); 141 } 142 if (nflag) { 143 printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 144 exit(0); 145 } 146 if ((namewanted) && (countfiles(namelist) == 0)) { 147 printf("no files for %s.\n", (argc == 1) ? 148 *argv : "specified users"); 149 exit(0); 150 } 151 printqueue(namelist); 152 exit(0); 153 } 154 155 /* 156 * Count the number of jobs in the spooling area owned by a certain person(s). 157 */ 158 countfiles(namelist) 159 char **namelist; 160 { 161 int i; /* for loop index */ 162 int entryfound; /* found file owned by user(s)*/ 163 int numfiles = 0; /* number of files owned by a 164 certain person(s) */ 165 char **ptr; /* scratch pointer */ 166 167 168 /* 169 * For each file in the queue, see if the user(s) own the file. We 170 * have to use "entryfound" (rather than simply incrementing "numfiles") 171 * so that if a person's name appears twice on the command line we 172 * don't double the number of files owned by him/her. 173 */ 174 for (i = 0; i < numentries ; i++) { 175 ptr = namelist; 176 entryfound = 0; 177 178 while (*ptr) { 179 if (isowner(*ptr,queue[i]->d_name)) 180 ++entryfound; 181 ++ptr; 182 } 183 if (entryfound) 184 ++numfiles; 185 } 186 return(numfiles); 187 } 188 189 /* 190 * Print the queue. If only jobs belonging to a certain person(s) are requested, 191 * only print jobs that belong to that person(s). 192 */ 193 printqueue(namelist) 194 char **namelist; 195 { 196 int i; /* for loop index */ 197 int rank = 1; /* rank of a job */ 198 int entryfound; /* found file owned by user(s)*/ 199 static int printrank(); /* print the rank of a job */ 200 int plastrun(); /* print the last time the 201 spooling area was updated */ 202 int powner(); /* print the name of the owner 203 of the job */ 204 char **ptr; /* scratch pointer */ 205 struct stat stbuf; /* buffer for file stats */ 206 207 208 /* 209 * Print the time the spooling area was last modified and the header 210 * for the queue. 211 */ 212 plastrun(); 213 printf(" Rank Execution Date Owner Job # Job Name\n"); 214 215 /* 216 * Print the queue. If a certain name(s) was requested, print only jobs 217 * belonging to that person(s), otherwise print the entire queue. 218 * Once again, we have to use "entryfound" (rather than simply 219 * comparing each command line argument) so that if a person's name 220 * appears twice we don't print each file owned by him/her twice. 221 * 222 * 223 * "printrank", "printdate", and "printjobname" all take existing 224 * data and display it in a friendly manner. 225 * 226 */ 227 for (i = 0; i < numentries; i++) { 228 if ((stat(queue[i]->d_name, &stbuf)) < 0) { 229 continue; 230 } 231 if (namewanted) { 232 ptr = namelist; 233 entryfound = 0; 234 235 while (*ptr) { 236 if (isowner(*ptr,queue[i]->d_name)) 237 ++entryfound; 238 ++ptr; 239 } 240 if (!entryfound) 241 continue; 242 } 243 printrank(rank++); 244 printdate(queue[i]->d_name); 245 powner(queue[i]->d_name); 246 printf("%5d",stbuf.st_ino); 247 printjobname(queue[i]->d_name); 248 } 249 ++ptr; 250 } 251 252 /* 253 * See if "name" owns "job". 254 */ 255 isowner(name,job) 256 char *name; 257 char *job; 258 { 259 char buf[128]; /* buffer for 1st line of spoolfile 260 header */ 261 FILE *infile; /* I/O stream to spoolfile */ 262 263 if ((infile = fopen(job,"r")) == NULL) { 264 fprintf(stderr,"Couldn't open spoolfile "); 265 perror(job); 266 return(0); 267 } 268 269 if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { 270 fclose(infile); 271 return(0); 272 } 273 274 fclose(infile); 275 return((strcmp(name,buf) == 0) ? 1 : 0); 276 } 277 278 /* 279 * Print the owner of the job. This is stored on the first line of the 280 * spoolfile. If we run into trouble getting the name, we'll just print "???". 281 */ 282 powner(file) 283 char *file; 284 { 285 char owner[10]; /* the owner */ 286 FILE *infile; /* I/O stream to spoolfile */ 287 288 /* 289 * Open the job file and grab the first line. 290 */ 291 292 if ((infile = fopen(file,"r")) == NULL) { 293 printf("%-10.9s","???"); 294 perror(file); 295 return; 296 } 297 298 if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) { 299 printf("%-10.9s","???"); 300 fclose(infile); 301 return; 302 } 303 304 fclose(infile); 305 printf("%-10.9s",owner); 306 307 } 308 309 /* 310 * Print the time the spooling area was updated. 311 */ 312 plastrun() 313 { 314 struct timeval now; /* time it is right now */ 315 struct timezone zone; /* NOT USED */ 316 struct tm *loc; /* detail of time it is right */ 317 time_t lasttime; /* last update time in seconds 318 since 1/1/70 */ 319 FILE *last; /* file where last update hour 320 is stored */ 321 322 323 /* 324 * Open the file where the last update time is stored, and grab the 325 * last update hour. The update time is measured in seconds since 326 * 1/1/70. 327 */ 328 if ((last = fopen(_PATH_LASTFILE,"r")) == NULL) { 329 perror(_PATH_LASTFILE); 330 exit(1); 331 } 332 fscanf(last,"%lu",&lasttime); 333 fclose(last); 334 335 /* 336 * Get a broken down representation of the last update time. 337 */ 338 loc = localtime(&lasttime); 339 340 /* 341 * Print the time that the spooling area was last updated. 342 */ 343 printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 344 printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 345 printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 346 } 347 348 /* 349 * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 350 */ 351 static 352 printrank(n) 353 { 354 static char *r[] = { 355 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 356 }; 357 358 if ((n/10) == 1) 359 printf("%3d%-5s", n,"th"); 360 else 361 printf("%3d%-5s", n, r[n%10]); 362 } 363 364 /* 365 * Print the date that a job is to be executed. This takes some manipulation 366 * of the file name. 367 */ 368 printdate(filename) 369 char *filename; 370 { 371 int yday = 0; /* day of year file will be 372 executed */ 373 int min = 0; /* min. file will be executed */ 374 int hour = 0; /* hour file will be executed */ 375 int day = 0; /* day file will be executed */ 376 int month = 0; /* month file will be executed*/ 377 int year = 0; /* year file will be executed */ 378 int get_mth_day(); /* convert a day of year to a 379 month and day of month */ 380 char date[19]; /* reformatted execution date */ 381 382 /* 383 * Pick off the necessary info from the file name and convert the day 384 * of year to a month and day of month. 385 */ 386 sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 387 get_mth_day(year,yday,&month,&day); 388 389 /* 390 * Format the execution date of a job. 391 */ 392 sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 393 day, year,hour,min); 394 395 /* 396 * Print the date the job will be executed. 397 */ 398 printf("%-21.18s",date); 399 } 400 401 /* 402 * Given a day of the year, calculate the month and day of month. 403 */ 404 get_mth_day(year,dayofyear,month,day) 405 int year, dayofyear, *month, *day; 406 407 { 408 409 int i = 1; /* for loop index */ 410 int leap; /* are we dealing with a leap 411 year? */ 412 /* Table of the number of days 413 in each month of the year. 414 415 dofy_tab[1] -- regular year 416 dofy_tab[2] -- leap year 417 */ 418 419 static int dofy_tab[2][13] = { 420 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 421 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 422 }; 423 424 /* 425 * Are we dealing with a leap year? 426 */ 427 leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 428 429 /* 430 * Calculate the month of the year and day of the month. 431 */ 432 while (dayofyear >= dofy_tab[leap][i]) { 433 dayofyear -= dofy_tab[leap][i++]; 434 ++(*month); 435 } 436 *day = (dayofyear + 1); 437 } 438 439 /* 440 * Print a job name. If the old "at" has been used to create the spoolfile, 441 * the three line header that the new version of "at" puts in the spoolfile. 442 * Thus, we just print "???". 443 */ 444 printjobname(file) 445 char *file; 446 { 447 char *ptr; /* scratch pointer */ 448 char jobname[28]; /* the job name */ 449 FILE *filename; /* job file in spooling area */ 450 451 /* 452 * Open the job file and grab the second line. 453 */ 454 printf(" "); 455 456 if ((filename = fopen(file,"r")) == NULL) { 457 printf("%.27s\n", "???"); 458 perror(file); 459 return; 460 } 461 /* 462 * Skip over the first line. 463 */ 464 fscanf(filename,"%*[^\n]\n"); 465 466 /* 467 * Now get the job name. 468 */ 469 if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) { 470 printf("%.27s\n", "???"); 471 fclose(filename); 472 return; 473 } 474 fclose(filename); 475 476 /* 477 * Put a pointer at the begining of the line and remove the basename 478 * from the job file. 479 */ 480 ptr = jobname; 481 if ((ptr = (char *)rindex(jobname,'/')) != 0) 482 ++ptr; 483 else 484 ptr = jobname; 485 486 if (strlen(ptr) > 23) 487 printf("%.23s ...\n",ptr); 488 else 489 printf("%.27s\n",ptr); 490 } 491 492 /* 493 * Do we want to include a file in the queue? (used by "scandir") We are looking 494 * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 495 * the file name has three dots in it. This test will suffice since the only 496 * other files in /usr/spool/at don't have any dots in their name. 497 */ 498 filewanted(direntry) 499 struct direct *direntry; 500 { 501 int numdot = 0; 502 char *filename; 503 504 filename = direntry->d_name; 505 while (*filename) 506 numdot += (*(filename++) == '.'); 507 return(numdot == 3); 508 } 509 510 /* 511 * Sort files by time of creation. (used by "scandir") 512 */ 513 creation(d1, d2) 514 struct direct **d1, **d2; 515 { 516 struct stat stbuf1, stbuf2; 517 518 if (stat((*d1)->d_name,&stbuf1) < 0) 519 return(1); 520 521 if (stat((*d2)->d_name,&stbuf2) < 0) 522 return(1); 523 524 return(stbuf1.st_ctime < stbuf2.st_ctime); 525 } 526 527 /* 528 * Print usage info and exit. 529 */ 530 usage() 531 { 532 fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 533 exit(1); 534 } 535