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