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