1/* statsb.c 2 System dependent routines for uustat. 3 4 Copyright (C) 1992, 1993, 1994 Ian Lance Taylor 5 6 This file is part of the Taylor UUCP package. 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of the 11 License, or (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 21 22 The author of the program may be contacted at ian@airs.com. 23 */ 24 25#include "uucp.h" 26 27#if USE_RCS_ID 28const char statsb_rcsid[] = "$Id: statsb.c,v 1.20 2002/03/05 19:10:42 ian Rel $"; 29#endif 30 31#include "uudefs.h" 32#include "uuconf.h" 33#include "sysdep.h" 34#include "system.h" 35 36#include <errno.h> 37 38#if HAVE_FCNTL_H 39#include <fcntl.h> 40#else 41#if HAVE_SYS_FILE_H 42#include <sys/file.h> 43#endif 44#endif 45 46#ifndef O_RDONLY 47#define O_RDONLY 0 48#define O_WRONLY 1 49#define O_RDWR 2 50#endif 51 52#ifndef O_NOCTTY 53#define O_NOCTTY 0 54#endif 55 56#if HAVE_OPENDIR 57#if HAVE_DIRENT_H 58#include <dirent.h> 59#else /* ! HAVE_DIRENT_H */ 60#include <sys/dir.h> 61#define dirent direct 62#endif /* ! HAVE_DIRENT_H */ 63#endif /* HAVE_OPENDIR */ 64 65#if HAVE_TIME_H 66#include <time.h> 67#endif 68 69#if HAVE_UTIME_H 70#include <utime.h> 71#endif 72 73/* Local functions. */ 74 75static int issettime P((const char *z, time_t inow)); 76static boolean fskill_or_rejuv P((pointer puuconf, const char *zid, 77 boolean fkill)); 78 79/* Set file access time to the present. On many systems this could be 80 done by passing NULL to utime, but on some that doesn't work. This 81 routine is not time critical, so we never rely on NULL. */ 82 83static int 84issettime(z, inow) 85 const char *z; 86 time_t inow; 87{ 88#if HAVE_UTIME_H 89 struct utimbuf s; 90 91 s.actime = inow; 92 s.modtime = inow; 93 return utime ((char *) z, &s); 94#else 95 time_t ai[2]; 96 97 ai[0] = inow; 98 ai[1] = inow; 99 return utime ((char *) z, ai); 100#endif 101} 102 103/* Kill a job, given the jobid. */ 104 105boolean 106fsysdep_kill_job (puuconf, zid) 107 pointer puuconf; 108 const char *zid; 109{ 110 return fskill_or_rejuv (puuconf, zid, TRUE); 111} 112 113/* Rejuvenate a job, given the jobid. */ 114 115boolean 116fsysdep_rejuvenate_job (puuconf, zid) 117 pointer puuconf; 118 const char *zid; 119{ 120 return fskill_or_rejuv (puuconf, zid, FALSE); 121} 122 123/* Kill or rejuvenate a job, given the jobid. */ 124 125static boolean 126fskill_or_rejuv (puuconf, zid, fkill) 127 pointer puuconf; 128 const char *zid; 129 boolean fkill; 130{ 131 char *zfile; 132 char *zsys; 133 char bgrade; 134 time_t inow = 0; 135 int iuuconf; 136 struct uuconf_system ssys; 137 FILE *e; 138 boolean fret; 139 char *zline; 140 size_t cline; 141 int isys; 142 143 zfile = zsjobid_to_file (zid, &zsys, &bgrade); 144 if (zfile == NULL) 145 return FALSE; 146 147 if (! fkill) 148 inow = time ((time_t *) NULL); 149 150 iuuconf = uuconf_system_info (puuconf, zsys, &ssys); 151 if (iuuconf == UUCONF_NOT_FOUND) 152 { 153 if (! funknown_system (puuconf, zsys, &ssys)) 154 { 155 ulog (LOG_ERROR, "%s: Bad job id", zid); 156 ubuffree (zfile); 157 ubuffree (zsys); 158 return FALSE; 159 } 160 } 161 else if (iuuconf != UUCONF_SUCCESS) 162 { 163 ulog_uuconf (LOG_ERROR, puuconf, iuuconf); 164 ubuffree (zfile); 165 ubuffree (zsys); 166 return FALSE; 167 } 168 169 e = fopen (zfile, "r"); 170 if (e == NULL) 171 { 172 if (errno == ENOENT) 173 ulog (LOG_ERROR, "%s: Job not found", zid); 174 else 175 ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno)); 176 (void) uuconf_system_free (puuconf, &ssys); 177 ubuffree (zfile); 178 ubuffree (zsys); 179 return FALSE; 180 } 181 182 /* Now we have to read through the file to identify any temporary 183 files. */ 184 fret = TRUE; 185 zline = NULL; 186 cline = 0; 187 while (getline (&zline, &cline, e) > 0) 188 { 189 struct scmd s; 190 191 if (! fparse_cmd (zline, &s)) 192 { 193 ulog (LOG_ERROR, "Bad line in command file %s", zfile); 194 fret = FALSE; 195 continue; 196 } 197 198 /* You are only permitted to delete a job if you submitted it or 199 if you are root or uucp. */ 200 if (strcmp (s.zuser, zsysdep_login_name ()) != 0 201 && ! fsysdep_privileged ()) 202 { 203 ulog (LOG_ERROR, "%s: Not submitted by you", zid); 204 xfree ((pointer) zline); 205 (void) fclose (e); 206 (void) uuconf_system_free (puuconf, &ssys); 207 ubuffree (zfile); 208 ubuffree (zsys); 209 return FALSE; 210 } 211 212 if (s.bcmd == 'S' || s.bcmd == 'E') 213 { 214 char *ztemp; 215 216 ztemp = zsfind_file (s.ztemp, ssys.uuconf_zname, bgrade); 217 if (ztemp == NULL) 218 fret = FALSE; 219 else 220 { 221 if (fkill) 222 isys = remove (ztemp); 223 else 224 isys = issettime (ztemp, inow); 225 226 if (isys != 0 && errno != ENOENT) 227 { 228 ulog (LOG_ERROR, "%s (%s): %s", 229 fkill ? "remove" : "utime", ztemp, 230 strerror (errno)); 231 fret = FALSE; 232 } 233 234 ubuffree (ztemp); 235 } 236 } 237 } 238 239 xfree ((pointer) zline); 240 (void) fclose (e); 241 (void) uuconf_system_free (puuconf, &ssys); 242 ubuffree (zsys); 243 244 if (fkill) 245 isys = remove (zfile); 246 else 247 isys = issettime (zfile, inow); 248 249 if (isys != 0 && errno != ENOENT) 250 { 251 ulog (LOG_ERROR, "%s (%s): %s", fkill ? "remove" : "utime", 252 zfile, strerror (errno)); 253 fret = FALSE; 254 } 255 256 ubuffree (zfile); 257 258 return fret; 259} 260 261/* Get the time a work job was queued. */ 262 263long 264ixsysdep_work_time (qsys, pseq) 265 const struct uuconf_system *qsys; 266 pointer pseq; 267{ 268 char *zjobid, *zfile; 269 long iret; 270 271 zjobid = zsysdep_jobid (qsys, pseq); 272 zfile = zsjobid_to_file (zjobid, (char **) NULL, (char *) NULL); 273 if (zfile == NULL) 274 return 0; 275 ubuffree (zjobid); 276 iret = ixsysdep_file_time (zfile); 277 ubuffree (zfile); 278 return iret; 279} 280 281/* Get the time a file was created (actually, the time it was last 282 modified). */ 283 284long 285ixsysdep_file_time (zfile) 286 const char *zfile; 287{ 288 struct stat s; 289 290 if (stat ((char *) zfile, &s) < 0) 291 { 292 if (errno != ENOENT) 293 ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno)); 294 return ixsysdep_time ((long *) NULL); 295 } 296 297 return (long) s.st_mtime; 298} 299 300/* Set the time of a file to the current time. */ 301 302boolean 303fsysdep_touch_file (zfile) 304 const char *zfile; 305{ 306 if (issettime (zfile, time ((time_t *) NULL)) != 0) 307 { 308 ulog (LOG_ERROR, "utime (%s): %s", zfile, strerror (errno)); 309 return FALSE; 310 } 311 312 return TRUE; 313} 314 315/* Start getting the status files. */ 316 317boolean 318fsysdep_all_status_init (phold) 319 pointer *phold; 320{ 321 DIR *qdir; 322 323 qdir = opendir ((char *) ".Status"); 324 if (qdir == NULL) 325 { 326 ulog (LOG_ERROR, "opendir (.Status): %s", strerror (errno)); 327 return FALSE; 328 } 329 330 *phold = (pointer) qdir; 331 return TRUE; 332} 333 334/* Get the next status file. */ 335 336char * 337zsysdep_all_status (phold, pferr, qstat) 338 pointer phold; 339 boolean *pferr; 340 struct sstatus *qstat; 341{ 342 DIR *qdir = (DIR *) phold; 343 struct dirent *qentry; 344 345 while (TRUE) 346 { 347 errno = 0; 348 qentry = readdir (qdir); 349 if (qentry == NULL) 350 { 351 if (errno == 0) 352 *pferr = FALSE; 353 else 354 { 355 ulog (LOG_ERROR, "readdir: %s", strerror (errno)); 356 *pferr = TRUE; 357 } 358 return NULL; 359 } 360 361 if (qentry->d_name[0] != '.') 362 { 363 struct uuconf_system ssys; 364 365 /* Hack seriously; fsysdep_get_status only looks at the 366 zname element of the qsys argument, so if we fake that we 367 can read the status file. This should really be done 368 differently. */ 369 ssys.uuconf_zname = qentry->d_name; 370 if (fsysdep_get_status (&ssys, qstat, (boolean *) NULL)) 371 return zbufcpy (qentry->d_name); 372 373 /* If fsysdep_get_status fails, it will output an error 374 message. We just continue with the next entry, so that 375 most of the status files will be displayed. */ 376 } 377 } 378} 379 380/* Finish getting the status file. */ 381 382void 383usysdep_all_status_free (phold) 384 pointer phold; 385{ 386 DIR *qdir = (DIR *) phold; 387 388 (void) closedir (qdir); 389} 390 391/* Get the status of all processes holding lock files. We do this by 392 invoking ps after we've figured out the process entries to use. */ 393 394boolean 395fsysdep_lock_status () 396{ 397 DIR *qdir; 398 struct dirent *qentry; 399 int calc; 400 pid_t *pai; 401#if HAVE_QNX_LOCKFILES 402 nid_t *painid; 403#endif 404 int cgot; 405 int aidescs[3]; 406 char *zcopy, *ztok; 407 int cargs, iarg; 408 char **pazargs; 409 410 qdir = opendir ((char *) zSlockdir); 411 if (qdir == NULL) 412 { 413 ulog (LOG_ERROR, "opendir (%s): %s", zSlockdir, strerror (errno)); 414 return FALSE; 415 } 416 417 /* We look for entries that start with "LCK.." and ignore everything 418 else. This won't find all possible lock files, but it should 419 find all the locks on terminals and systems. */ 420 421 calc = 0; 422 pai = NULL; 423 cgot = 0; 424#if HAVE_QNX_LOCKFILES 425 painid = NULL; 426#endif 427 while ((qentry = readdir (qdir)) != NULL) 428 { 429 char *zname; 430 int o; 431#if HAVE_QNX_LOCKFILES 432 nid_t inid; 433 char ab[23]; 434 char *zend; 435#else 436#if HAVE_V2_LOCKFILES 437 int i; 438#else 439 char ab[12]; 440#endif 441#endif 442 int cread; 443 int ierr; 444 pid_t ipid; 445 int icheck; 446 447 if (strncmp (qentry->d_name, "LCK..", sizeof "LCK.." - 1) != 0) 448 continue; 449 450 zname = zsysdep_in_dir (zSlockdir, qentry->d_name); 451 o = open ((char *) zname, O_RDONLY | O_NOCTTY, 0); 452 if (o < 0) 453 { 454 if (errno != ENOENT) 455 ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno)); 456 ubuffree (zname); 457 continue; 458 } 459 460#if HAVE_V2_LOCKFILES 461 cread = read (o, &i, sizeof i); 462#else 463 cread = read (o, ab, sizeof ab - 1); 464#endif 465 466 ierr = errno; 467 (void) close (o); 468 469 if (cread < 0) 470 { 471 ulog (LOG_ERROR, "read %s: %s", zname, strerror (ierr)); 472 ubuffree (zname); 473 continue; 474 } 475 476 ubuffree (zname); 477 478#if HAVE_QNX_LOCKFILES 479 ab[cread] = '\0'; 480 ipid = (pid_t) strtol (ab, &zend, 10); 481 inid = (nid_t) strtol (zend, (char **) NULL, 10); 482#else 483#if HAVE_V2_LOCKFILES 484 ipid = (pid_t) i; 485#else 486 ab[cread] = '\0'; 487 ipid = (pid_t) strtol (ab, (char **) NULL, 10); 488#endif 489#endif 490 491#if HAVE_QNX_LOCKFILES 492 printf ("%s: %ld %ld\n", qentry->d_name, (long) inid, (long) ipid); 493#else 494 printf ("%s: %ld\n", qentry->d_name, (long) ipid); 495#endif 496 497 for (icheck = 0; icheck < cgot; icheck++) 498 if (pai[icheck] == ipid) 499 break; 500 if (icheck < cgot) 501 continue; 502 503 if (cgot >= calc) 504 { 505 calc += 10; 506 pai = (pid_t *) xrealloc ((pointer) pai, calc * sizeof (pid_t)); 507#if HAVE_QNX_LOCKFILES 508 painid = (nid_t *) xrealloc ((pointer) painid, 509 calc * sizeof (nid_t)); 510#endif 511 } 512 513 pai[cgot] = ipid; 514#if HAVE_QNX_LOCKFILES 515 painid[cgot] = inid; 516#endif 517 ++cgot; 518 } 519 520 if (cgot == 0) 521 return TRUE; 522 523 aidescs[0] = SPAWN_NULL; 524 aidescs[1] = 1; 525 aidescs[2] = 2; 526 527 /* Parse PS_PROGRAM into an array of arguments. */ 528 zcopy = zbufcpy (PS_PROGRAM); 529 530 cargs = 0; 531 for (ztok = strtok (zcopy, " \t"); 532 ztok != NULL; 533 ztok = strtok ((char *) NULL, " \t")) 534 ++cargs; 535 536 pazargs = (char **) xmalloc ((cargs + 1) * sizeof (char *)); 537 538 memcpy (zcopy, PS_PROGRAM, sizeof PS_PROGRAM); 539 for (ztok = strtok (zcopy, " \t"), iarg = 0; 540 ztok != NULL; 541 ztok = strtok ((char *) NULL, " \t"), ++iarg) 542 pazargs[iarg] = ztok; 543 pazargs[iarg] = NULL; 544 545#if ! HAVE_PS_MULTIPLE 546 /* We have to invoke ps multiple times. */ 547 { 548 int i; 549 char *zlast, *zset; 550#if HAVE_QNX_LOCKFILES 551 char *zpenultimate, *zsetnid; 552#endif /* HAVE_QNX_LOCKFILES */ 553 554 zlast = pazargs[cargs - 1]; 555 zset = zbufalc (strlen (zlast) + 20); 556 557#if HAVE_QNX_LOCKFILES 558 /* We assume in this case that PS_PROGRAM ends with " -n -p". 559 Thus, the last argument is "-p" and the second-to-last 560 (penultimate) argument is "-n". We modify them to read "-n###" 561 and "-p###" where "###" is the node ID and the process ID, 562 respectively. This seems like quite a roundabout way of doing 563 things. Why don't we just leave the " -n -p" part out of 564 PS_PROGRAM and construct the "-n###" and "-p###" arguments here 565 from scratch? Because that would not fit as well with how the 566 code works for the other systems and would require larger 567 changes. */ 568 zpenultimate = pazargs[cargs - 2]; 569 zsetnid = zbufalc (strlen (zpenultimate) + 20); 570#endif 571 572 for (i = 0; i < cgot; i++) 573 { 574 pid_t ipid; 575 576 sprintf (zset, "%s%ld", zlast, (long) pai[i]); 577 pazargs[cargs - 1] = zset; 578 579#if HAVE_QNX_LOCKFILES 580 sprintf (zsetnid, "%s%ld", zpenultimate, (long) painid[i]); 581 pazargs[cargs - 2] = zsetnid; 582#endif 583 584 ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, 585 (const char *) NULL, FALSE, TRUE, 586 (const char *) NULL, (const char *) NULL, 587 (const char *) NULL); 588 if (ipid < 0) 589 ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); 590 else 591 (void) ixswait ((unsigned long) ipid, PS_PROGRAM); 592 } 593 ubuffree (zset); 594#if HAVE_QNX_LOCKFILES 595 ubuffree (zsetnid); 596#endif 597 } 598#else 599 { 600 char *zlast; 601 int i; 602 pid_t ipid; 603 604 zlast = zbufalc (strlen (pazargs[cargs - 1]) + cgot * 20 + 1); 605 strcpy (zlast, pazargs[cargs - 1]); 606 for (i = 0; i < cgot; i++) 607 { 608 char ab[20]; 609 610 sprintf (ab, "%ld", (long) pai[i]); 611 strcat (zlast, ab); 612 if (i + 1 < cgot) 613 strcat (zlast, ","); 614 } 615 pazargs[cargs - 1] = zlast; 616 617 ipid = ixsspawn ((const char **) pazargs, aidescs, FALSE, FALSE, 618 (const char *) NULL, FALSE, TRUE, 619 (const char *) NULL, (const char *) NULL, 620 (const char *) NULL); 621 if (ipid < 0) 622 ulog (LOG_ERROR, "ixsspawn: %s", strerror (errno)); 623 else 624 (void) ixswait ((unsigned long) ipid, PS_PROGRAM); 625 ubuffree (zlast); 626 } 627#endif 628 629 ubuffree (zcopy); 630 xfree ((pointer) pazargs); 631 632 return TRUE; 633} 634