1/* uustat.c
2   UUCP status program
3
4   Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 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 uustat_rcsid[] = "$Id: uustat.c,v 1.61 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#include <ctype.h>
32#include <errno.h>
33
34#if TM_IN_SYS_TIME
35#include <sys/time.h>
36#else
37#include <time.h>
38#endif
39
40#include "getopt.h"
41
42#include "uudefs.h"
43#include "uuconf.h"
44#include "system.h"
45
46/* The uustat program permits various listings and manipulations of
47   files in the spool directory.  This implementation supports the
48   following switches:
49
50   -a list all jobs
51   -Blines number of lines of standard input to mail
52   -ccommand list only executions of specified command
53   -Ccommand list only jobs other than executions of specified command
54   -e list execute jobs rather than command requests
55   -i ask user whether to kill each listed job
56   -Ifile set configuration file name
57   -kjobid kill job with specified ID
58   -K kill each listed job
59   -m report status for all remote machines
60   -M mail uucp about each job killed with -K
61   -N mail requestor about each job killed with -K
62   -ohour report jobs older than specified number of hours
63   -p do "ps -flp" on all processes holding lock files (Unix specific)
64   -q list number of jobs for all systems
65   -Q don't list jobs, just do -K processing
66   -rjobid rejuvenate job with specified ID
67   -ssystem report on all jobs for specified system
68   -Ssystem report on all jobs other than for specified system
69   -uuser report on all jobs for specified user
70   -Uuser report on all jobs other than for specified user
71   -Wcomment comment to include in mail messages
72   -xdebug set debugging level
73   -yhour report jobs younger than specified number of hours  */
74
75/* What to do with a job that matches the selection criteria; these
76   values may be or'red together.  */
77#define JOB_SHOW (01)
78#define JOB_INQUIRE (02)
79#define JOB_KILL (04)
80#define JOB_REJUVENATE (010)
81#define JOB_MAIL (020)
82#define JOB_NOTIFY (040)
83
84/* This structure is used to accumulate all the lines in a single
85   command file, so that they can all be displayed at once and so that
86   executions can be displayed reasonably.  */
87
88struct scmdlist
89{
90  struct scmdlist *qnext;
91  struct scmd s;
92  long itime;
93};
94
95/* Local functions.  */
96
97static void ususage P((void));
98static void ushelp P((void));
99static boolean fsxqt_file_read P((pointer puuconf, FILE *));
100static void usxqt_file_free P((void));
101static int isxqt_cmd P((pointer puuconf, int argc, char **argv, pointer pvar,
102			pointer pinfo));
103static int isxqt_file P((pointer puuconf, int argc, char **argv, pointer pvar,
104			 pointer pinfo));
105static int isxqt_user P((pointer puuconf, int argc, char **argv, pointer pvar,
106			 pointer pinfo));
107static boolean fsworkfiles P((pointer puuconf, int icmd, int csystems,
108			      char **pazsystems, boolean fnotsystems,
109			      int cusers, char **pazusers,
110			      boolean fnotusers, long iold, long iyoung,
111			      int ccommands, char **pazcommands,
112			      boolean fnotcommands, const char *zcomment,
113			      int cstdin));
114static boolean fsworkfiles_system P((pointer puuconf,int icmd,
115				     const struct uuconf_system *qsys,
116				     int cusers,  char **pazusers,
117				     boolean fnotusers, long iold,
118				     long iyoung, int ccommands,
119				     char **pazcommands,
120				     boolean fnotcommands,
121				     const char *zcomment, int cstdin));
122static boolean fsworkfile_show P((pointer puuconf, int icmd,
123				  const struct uuconf_system *qsys,
124				  const struct scmd *qcmd,
125				  long itime, int ccommands,
126				  char **pazcommands, boolean fnotcommands,
127				  const char *zcomment, int cstdin));
128static void usworkfile_header P((const struct uuconf_system *qsys,
129				 const struct scmd *qcmd,
130				 const char *zjobid,
131				 long itime, boolean ffirst));
132static boolean fsexecutions P((pointer puuconf, int icmd, int csystems,
133			       char **pazsystems, boolean fnotsystems,
134			       int cusers, char **pazusers,
135			       boolean fnotusers, long iold, long iyoung,
136			       int ccommands, char **pazcommands,
137			       boolean fnotcommands, const char *zcomment,
138			       int cstdin));
139static boolean fsnotify P((pointer puuconf, int icmd, const char *zcomment,
140			   int cstdin, boolean fkilled, const char *zcmd,
141			   struct scmdlist *qcmd, const char *zid,
142			   long itime, const char *zuser,
143			   const struct uuconf_system *qsys,
144			   const char *zstdin, pointer pstdinseq,
145			   const char *zrequestor));
146static boolean fsquery P((pointer puuconf, int csystems,
147			  char **pazsystems, boolean fnotsystems,
148			  long iold, long iyoung));
149static int csunits_show P((long idiff));
150static boolean fsmachines P((void));
151
152/* Long getopt options.  */
153static const struct option asSlongopts[] =
154{
155  { "all", no_argument, NULL, 'a' },
156  { "mail-lines", required_argument, NULL, 'B' },
157  { "command", required_argument, NULL, 'c' },
158  { "not-command", required_argument, NULL, 'C' },
159  { "executions", no_argument, NULL, 'e' },
160  { "prompt", no_argument, NULL, 'i' },
161  { "kill", required_argument, NULL, 'k' },
162  { "kill-all", no_argument, NULL, 'K' },
163  { "status", no_argument, NULL, 'm' },
164  { "mail", no_argument, NULL, 'M' },
165  { "notify", no_argument, NULL, 'N' },
166  { "older-than", required_argument, NULL, 'o' },
167  { "ps", no_argument, NULL, 'p' },
168  { "list", no_argument, NULL, 'q' },
169  { "no-list", no_argument, NULL, 'Q' },
170  { "rejuvenate", required_argument, NULL, 'r' },
171  { "rejuvenate-all", no_argument, NULL, 'R' },
172  { "system", required_argument, NULL, 's' },
173  { "not-system", required_argument, NULL, 'S' },
174  { "user", required_argument, NULL, 'u' },
175  { "not-user", required_argument, NULL, 'U' },
176  { "comment", required_argument, NULL, 'W' },
177  { "younger-than", required_argument, NULL, 'y' },
178  { "config", required_argument, NULL, 'I' },
179  { "debug", required_argument, NULL, 'x' },
180  { "version", no_argument, NULL, 'v' },
181  { "help", no_argument, NULL, 1 },
182  { NULL, 0, NULL, 0 }
183};
184
185int
186main (argc, argv)
187     int argc;
188     char **argv;
189{
190  /* -a: list all jobs.  */
191  boolean fall = FALSE;
192  /* -B lines: number of lines of standard input to mail.  */
193  int cstdin = 100;
194  /* -c,-C command: list only specified command.  */
195  int ccommands = 0;
196  char **pazcommands = NULL;
197  boolean fnotcommands = FALSE;
198  /* -e: list execute jobs.  */
199  boolean fexecute = FALSE;
200  /* -k jobid: kill specified job.  */
201  int ckills = 0;
202  char **pazkills = NULL;
203  /* -m: report machine status.  */
204  boolean fmachine = FALSE;
205  /* -o hour: report jobs older than given number of hours.  */
206  int ioldhours = -1;
207  /* -p: report status of jobs holding lock files.  */
208  boolean fps = FALSE;
209  /* -q: list number of jobs for each system.  */
210  boolean fquery = FALSE;
211  /* -r jobid: rejuvenate specified job.  */
212  int crejuvs = 0;
213  char **pazrejuvs = NULL;
214  /* -s,-S system: list all jobs for specified system.  */
215  int csystems = 0;
216  char **pazsystems = NULL;
217  boolean fnotsystems = FALSE;
218  /* -u,-U user: list all jobs for specified user.  */
219  int cusers = 0;
220  char **pazusers = NULL;
221  boolean fnotusers = FALSE;
222  /* -W comment: comment to include in mail messages.  */
223  const char *zcomment = NULL;
224  /* -y hour: report jobs younger than given number of hours.  */
225  int iyounghours = -1;
226  /* -I file: set configuration file.  */
227  const char *zconfig = NULL;
228  /* -Q, -i, -K, -M, -N: what to do with each job.  */
229  int icmd = JOB_SHOW;
230  int ccmds;
231  int iopt;
232  pointer puuconf;
233  int iuuconf;
234  long iold;
235  long iyoung;
236  const char *azoneuser[1];
237  boolean fret;
238
239  if (argc < 1)
240  {
241      zProgram = "uustat";
242      ususage ();
243  }
244
245  zProgram = argv[0];
246
247  while ((iopt = getopt_long (argc, argv,
248			      "aB:c:C:eiI:k:KmMNo:pqQr:Rs:S:u:U:vW:x:y:",
249			      asSlongopts, (int *) NULL)) != EOF)
250    {
251      switch (iopt)
252	{
253	case 'a':
254	  /* List all jobs.  */
255	  fall = TRUE;
256	  break;
257
258	case 'B':
259	  /* Number of lines of standard input to mail.  */
260	  cstdin = (int) strtol (optarg, (char **) NULL, 10);
261	  break;
262
263	case 'C':
264	  /* List jobs for other than specified command.  */
265	  fnotcommands = TRUE;
266	  /* Fall through.  */
267	case 'c':
268	  /* List specified command.  */
269	  ++ccommands;
270	  pazcommands = (char **) xrealloc ((pointer) pazcommands,
271					    ccommands * sizeof (char *));
272	  pazcommands[ccommands - 1] = optarg;
273	  break;
274
275	case 'e':
276	  /* List execute jobs.  */
277	  fexecute = TRUE;
278	  break;
279
280	case 'i':
281	  /* Prompt the user whether to kill each job.  */
282	  icmd |= JOB_INQUIRE;
283	  break;
284
285	case 'I':
286	  /* Set configuration file name.  */
287	  if (fsysdep_other_config (optarg))
288	    zconfig = optarg;
289	  break;
290
291	case 'k':
292	  /* Kill specified job.  */
293	  ++ckills;
294	  pazkills = (char **) xrealloc ((pointer) pazkills,
295					 ckills * sizeof (char *));
296	  pazkills[ckills - 1] = optarg;
297	  break;
298
299	case 'K':
300	  /* Kill each listed job.  */
301	  icmd |= JOB_KILL;
302	  break;
303
304	case 'm':
305	  /* Report machine status.  */
306	  fmachine = TRUE;
307	  break;
308
309	case 'M':
310	  /* Mail to uucp action taken on each job.  */
311	  icmd |= JOB_MAIL;
312	  break;
313
314	case 'N':
315	  /*  Mail to requestor action taken on each job.  */
316	  icmd |= JOB_NOTIFY;
317	  break;
318
319	case 'o':
320	  /* Report old jobs.  */
321	  ioldhours = (int) strtol (optarg, (char **) NULL, 10);
322	  break;
323
324	case 'p':
325	  /* Get status of processes holding locks.  */
326	  fps = TRUE;
327	  break;
328
329	case 'q':
330	  /* List number of jobs for each system.  */
331	  fquery = TRUE;
332	  break;
333
334	case 'Q':
335	  /* Don't list jobs, just do -K processing.  */
336	  icmd &=~ JOB_SHOW;
337	  break;
338
339	case 'r':
340	  /* Rejuvenate specified job.  */
341	  ++crejuvs;
342	  pazrejuvs = (char **) xrealloc ((pointer) pazrejuvs,
343					  crejuvs * sizeof (char *));
344	  pazrejuvs[crejuvs - 1] = optarg;
345	  break;
346
347	case 'R':
348	  /* Rejuvenate each listed job.  */
349	  icmd |= JOB_REJUVENATE;
350	  break;
351
352	case 'S':
353	  /* List jobs for other than specified system.  */
354	  fnotsystems = TRUE;
355	  /* Fall through.  */
356	case 's':
357	  /* List jobs for specified system.  */
358	  ++csystems;
359	  pazsystems = (char **) xrealloc ((pointer) pazsystems,
360					   csystems * sizeof (char *));
361	  pazsystems[csystems - 1] = optarg;
362	  break;
363
364	case 'U':
365	  /* List jobs for other than specified user.  */
366	  fnotusers = TRUE;
367	  /* Fall through.  */
368	case 'u':
369	  /* List jobs for specified user.  */
370	  ++cusers;
371	  pazusers = (char **) xrealloc ((pointer) pazusers,
372					 cusers * sizeof (char *));
373	  pazusers[cusers - 1] = optarg;
374	  break;
375
376	case 'W':
377	  /* Comment to include in mail messages.  */
378	  zcomment = optarg;
379	  break;
380
381	case 'x':
382#if DEBUG > 1
383	  /* Set debugging level.  */
384	  iDebug |= idebug_parse (optarg);
385#endif
386	  break;
387
388	case 'y':
389	  /* List jobs younger than given number of hours.  */
390	  iyounghours = (int) strtol (optarg, (char **) NULL, 10);
391	  break;
392
393	case 'v':
394	  /* Print version and exit.  */
395	  printf ("uustat (Taylor UUCP) %s\n", VERSION);
396	  printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n");
397	  printf ("This program is free software; you may redistribute it under the terms of\n");
398	  printf ("the GNU General Public LIcense.  This program has ABSOLUTELY NO WARRANTY.\n");
399	  exit (EXIT_SUCCESS);
400	  /*NOTREACHED*/
401
402	case 1:
403	  /* --help.  */
404	  ushelp ();
405	  exit (EXIT_SUCCESS);
406	  /*NOTREACHED*/
407
408	case 0:
409	  /* Long option found and flag set.  */
410	  break;
411
412	default:
413	  ususage ();
414	  /*NOTREACHED*/
415	}
416    }
417
418  if (optind != argc)
419    ususage ();
420
421  /* To avoid confusion, most options are only permitted by
422     themselves.  This restriction might be removed later, but it is
423     imposed by most implementations.  We do permit any combination of
424     -c, -s, -u, -o and -y, and any combination of -k and -r.  */
425  ccmds = 0;
426  if (fall)
427    ++ccmds;
428  if (ckills > 0 || crejuvs > 0)
429    ++ccmds;
430  if (fmachine)
431    ++ccmds;
432  if (fps)
433    ++ccmds;
434  if (fexecute || fquery || csystems > 0 || cusers > 0 || ioldhours != -1
435      || iyounghours != -1 || ccommands > 0)
436    ++ccmds;
437  if (fexecute && fquery)
438    ++ccmds;
439
440  if (ccmds > 1)
441    {
442      fprintf (stderr, "%s: too many options\n", zProgram);
443      ususage ();
444    }
445
446  if ((icmd & JOB_KILL) != 0
447      && (icmd & JOB_REJUVENATE) != 0)
448    {
449      fprintf (stderr, "%s: can not both rejuvenate and kill jobs\n",
450	       zProgram);
451      ususage ();
452    }
453
454  iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
455  if (iuuconf != UUCONF_SUCCESS)
456    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
457
458#if DEBUG > 1
459  {
460    const char *zdebug;
461
462    iuuconf = uuconf_debuglevel (puuconf, &zdebug);
463    if (iuuconf != UUCONF_SUCCESS)
464      ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
465    if (zdebug != NULL)
466      iDebug |= idebug_parse (zdebug);
467  }
468#endif
469
470  usysdep_initialize (puuconf, INIT_SUID);
471
472  /* If no commands were specified, we list all commands for the given
473     user.  */
474  if (ccmds == 0)
475    {
476      cusers = 1;
477      azoneuser[0] = zsysdep_login_name ();
478      pazusers = (char **) azoneuser;
479    }
480
481  /* Canonicalize the system names.  */
482  if (csystems > 0)
483    {
484      int i;
485
486      for (i = 0; i < csystems; i++)
487	{
488	  struct uuconf_system ssys;
489
490	  iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys);
491	  if (iuuconf != UUCONF_SUCCESS)
492	    {
493	      if (iuuconf == UUCONF_NOT_FOUND)
494		ulog (LOG_FATAL, "%s: System not found", pazsystems[i]);
495	      else
496		ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
497	    }
498	  if (strcmp (pazsystems[i], ssys.uuconf_zname) != 0)
499	    pazsystems[i] = zbufcpy (ssys.uuconf_zname);
500	  (void) uuconf_system_free (puuconf, &ssys);
501	}
502    }
503
504  if (ioldhours == -1)
505    iold = (long) -1;
506  else
507    {
508      iold = (ixsysdep_time ((long *) NULL)
509	      - (long) ioldhours * (long) 60 * (long) 60);
510      if (iold < 0L)
511	iold = 0L;
512    }
513  if (iyounghours == -1)
514    iyoung = (long) -1;
515  else
516    {
517      iyoung = (ixsysdep_time ((long *) NULL)
518		- (long) iyounghours * (long) 60 * (long) 60);
519      if (iyoung < 0L)
520	iyoung = 0L;
521    }
522
523  if (! fexecute
524      && ! fquery
525      && (fall
526	  || csystems > 0
527	  || cusers > 0
528	  || ioldhours != -1
529	  || iyounghours != -1
530	  || ccommands > 0))
531    fret = fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems,
532			cusers, pazusers, fnotusers, iold,  iyoung,
533			ccommands, pazcommands, fnotcommands, zcomment,
534			cstdin);
535  else if (fexecute)
536    fret = fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems,
537			 cusers, pazusers, fnotusers, iold, iyoung,
538			 ccommands, pazcommands, fnotcommands, zcomment,
539			 cstdin);
540  else if (icmd != JOB_SHOW)
541    {
542      ulog (LOG_ERROR,
543	    "-i, -K, -M, -N, -Q, -R not supported with -k, -m, -p, -q, -r");
544      ususage ();
545      fret = FALSE;
546    }
547  else if (fquery)
548    {
549      if (cusers > 0 || ccommands > 0)
550	{
551	  ulog (LOG_ERROR, "-u, -c not supported with -q");
552	  ususage ();
553	  fret = FALSE;
554	}
555      else
556	fret = fsquery (puuconf, csystems, pazsystems, fnotsystems,
557			iold, iyoung);
558    }
559  else if (fmachine)
560    fret = fsmachines ();
561  else if (ckills > 0 || crejuvs > 0)
562    {
563      int i;
564
565      fret = TRUE;
566      for (i = 0; i < ckills; i++)
567	if (! fsysdep_kill_job (puuconf, pazkills[i]))
568	  fret = FALSE;
569
570      for (i = 0; i < crejuvs; i++)
571	if (! fsysdep_rejuvenate_job (puuconf, pazrejuvs[i]))
572	  fret = FALSE;
573    }
574  else if (fps)
575    fret = fsysdep_lock_status ();
576  else
577    {
578#if DEBUG > 0
579      ulog (LOG_FATAL, "Can't happen");
580#endif
581      fret = FALSE;
582    }
583
584  ulog_close ();
585
586  usysdep_exit (fret);
587
588  /* Avoid errors about not returning a value.  */
589  return 0;
590}
591
592/* Print a usage message and die.  */
593
594static void
595ususage ()
596{
597  fprintf (stderr, "Usage: %s [options]\n", zProgram);
598  fprintf (stderr, "Use %s --help for help\n", zProgram);
599  exit (EXIT_FAILURE);
600}
601
602/* Print a help message.  */
603
604static void
605ushelp ()
606{
607  printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n",
608	  VERSION);
609  printf ("Usage: %s [options]\n", zProgram);
610  printf (" -a,--all: list all UUCP jobs\n");
611  printf (" -B,--mail-lines num: number of lines to return in -M or -N mail message\n");
612  printf (" -c,--command command: list requests for named command\n");
613  printf (" -C,--not-command command: list requests for other than named command\n");
614  printf (" -e,--executions: list queued executions rather than job requests\n");
615  printf (" -i,--prompt: prompt for whether to kill each listed job\n");
616  printf (" -k,--kill job: kill specified UUCP job\n");
617  printf (" -K,--kill-all: kill each listed job\n");
618  printf (" -m,--status: report status for all remote machines\n");
619  printf (" -M,--mail: mail report on each listed job to UUCP administrator\n");
620  printf (" -N,--notify: mail report on each listed job to requestor\n");
621  printf (" -o,--older-than hours: list all jobs older than given number of hours\n");
622  printf (" -p,--ps: show status of all processes holding UUCP locks\n");
623  printf (" -q,--list: list number of jobs for each system\n");
624  printf (" -Q,--no-list: don't list jobs, just take actions (-i, -K, -M, -N)\n");
625  printf (" -r,--rejuvenate job: rejuvenate specified UUCP job\n");
626  printf (" -R,--rejuvenate-all: rejuvenate each listed job\n");
627  printf (" -s,--system system: list all jobs for specified system\n");
628  printf (" -S,--not-system system: list all jobs for other than specified system\n");
629  printf (" -u,--user user: list all jobs for specified user\n");
630  printf (" -U,--not-user user: list all jobs for other than specified user\n");
631  printf (" -W,--comment comment: comment to include in mail messages\n");
632  printf (" -y,--younger-than hours: list all jobs younger than given number of hours\n");
633  printf (" -x,--debug debug: Set debugging level\n");
634#if HAVE_TAYLOR_CONFIG
635  printf (" -I,--config file: Set configuration file to use\n");
636#endif /* HAVE_TAYLOR_CONFIG */
637  printf (" -v,--version: Print version and exit\n");
638  printf (" --help: Print help and exit\n");
639  printf ("Report bugs to taylor-uucp@gnu.org\n");
640}
641
642/* We need to be able to read information from an execution file.  */
643
644/* The user name extracted from an execution file.  */
645static char *zSxqt_user;
646
647/* The system name from an execution file.  */
648static char *zSxqt_system;
649
650/* Address of requesting user (who to send mail to).  */
651static const char *zSxqt_requestor;
652
653/* The command (no arguments) from an execution file.  */
654static char *zSxqt_prog;
655
656/* The full command line from an execution file.  */
657static char *zSxqt_cmd;
658
659/* Number of files associated with an execution file.  */
660static int cSxqt_files;
661
662/* Names of files associated with execution file.  */
663static char **pazSxqt_files;
664
665/* Standard input file name.  */
666static const char *zSxqt_stdin;
667
668/* A command table used to dispatch an execution file.  */
669static const struct uuconf_cmdtab asSxqt_cmds[] =
670{
671  { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_cmd },
672  { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_stdin, NULL },
673  { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, isxqt_file },
674  { "R", UUCONF_CMDTABTYPE_STRING, (pointer) &zSxqt_requestor, NULL },
675  { "U", UUCONF_CMDTABTYPE_FN | 3, NULL, isxqt_user },
676  { NULL, 0, NULL, NULL }
677};
678
679/* Read an execution file, setting the above variables.  */
680
681static boolean
682fsxqt_file_read (puuconf, e)
683     pointer puuconf;
684     FILE *e;
685{
686  int iuuconf;
687  boolean fret;
688
689  zSxqt_user = NULL;
690  zSxqt_system = NULL;
691  zSxqt_stdin = NULL;
692  zSxqt_requestor = NULL;
693  zSxqt_prog = NULL;
694  zSxqt_cmd = NULL;
695  cSxqt_files = 0;
696  pazSxqt_files = NULL;
697
698  iuuconf = uuconf_cmd_file (puuconf, e, asSxqt_cmds, (pointer) NULL,
699			     (uuconf_cmdtabfn) NULL,
700			     UUCONF_CMDTABFLAG_CASE, (pointer) NULL);
701  if (iuuconf == UUCONF_SUCCESS)
702    fret = TRUE;
703  else
704    {
705      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
706      fret = FALSE;
707    }
708
709  if (zSxqt_user == NULL)
710    zSxqt_user = zbufcpy ("*unknown*");
711  if (zSxqt_system == NULL)
712    zSxqt_system = zbufcpy ("*unknown*");
713  if (zSxqt_prog == NULL)
714    {
715      zSxqt_prog = zbufcpy ("*none*");
716      zSxqt_cmd = zbufcpy ("*none*");
717    }
718
719  return fret;
720}
721
722/* Free up the information read from an execution file.  */
723
724static void
725usxqt_file_free ()
726{
727  int i;
728
729  ubuffree (zSxqt_user);
730  zSxqt_user = NULL;
731  ubuffree (zSxqt_system);
732  zSxqt_system = NULL;
733  ubuffree (zSxqt_prog);
734  zSxqt_prog = NULL;
735  ubuffree (zSxqt_cmd);
736  zSxqt_cmd = NULL;
737  for (i = 0; i < cSxqt_files; i++)
738    ubuffree (pazSxqt_files[i]);
739  cSxqt_files = 0;
740  xfree ((pointer) pazSxqt_files);
741  pazSxqt_files = NULL;
742  zSxqt_stdin = NULL;
743  zSxqt_requestor = NULL;
744}
745
746/* Get the command from an execution file.  */
747
748/*ARGSUSED*/
749static int
750isxqt_cmd (puuconf, argc, argv, pvar, pinfo)
751     pointer puuconf ATTRIBUTE_UNUSED;
752     int argc;
753     char **argv;
754     pointer pvar ATTRIBUTE_UNUSED;
755     pointer pinfo ATTRIBUTE_UNUSED;
756{
757  size_t clen;
758  int i;
759
760  if (argc <= 1)
761    return UUCONF_CMDTABRET_CONTINUE;
762
763  zSxqt_prog = zbufcpy (argv[1]);
764
765  clen = 0;
766  for (i = 1; i < argc; i++)
767    clen += strlen (argv[i]) + 1;
768
769  zSxqt_cmd = zbufalc (clen);
770  zSxqt_cmd[0] = '\0';
771  for (i = 1; i < argc - 1; i++)
772    {
773      strcat (zSxqt_cmd, argv[i]);
774      strcat (zSxqt_cmd, " ");
775    }
776  strcat (zSxqt_cmd, argv[i]);
777
778  return UUCONF_CMDTABRET_CONTINUE;
779}
780
781/* Get the associated files from an execution file.  */
782
783/*ARGSUSED*/
784static int
785isxqt_file (puuconf, argc, argv, pvar, pinfo)
786     pointer puuconf ATTRIBUTE_UNUSED;
787     int argc;
788     char **argv;
789     pointer pvar ATTRIBUTE_UNUSED;
790     pointer pinfo ATTRIBUTE_UNUSED;
791{
792  if (argc != 2 && argc != 3)
793    return UUCONF_CMDTABRET_CONTINUE;
794
795  /* If this file is not in the spool directory, just ignore it.  */
796  if (! fspool_file (argv[1]))
797    return UUCONF_CMDTABRET_CONTINUE;
798
799  ++cSxqt_files;
800  pazSxqt_files = (char **) xrealloc ((pointer) pazSxqt_files,
801				      cSxqt_files * sizeof (char *));
802
803  pazSxqt_files[cSxqt_files - 1] = zbufcpy (argv[1]);
804
805  return UUCONF_CMDTABRET_CONTINUE;
806}
807
808/* Get the requesting user and system from an execution file.  */
809
810/*ARGSUSED*/
811static int
812isxqt_user (puuconf, argc, argv, pvar, pinfo)
813     pointer puuconf ATTRIBUTE_UNUSED;
814     int argc ATTRIBUTE_UNUSED;
815     char **argv;
816     pointer pvar ATTRIBUTE_UNUSED;
817     pointer pinfo ATTRIBUTE_UNUSED;
818{
819  zSxqt_user = zbufcpy (argv[1]);
820  zSxqt_system = zbufcpy (argv[2]);
821  return UUCONF_CMDTABRET_CONTINUE;
822}
823
824/* Handle various possible requests to look at work files.  */
825
826static boolean
827fsworkfiles (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers,
828	     pazusers, fnotusers, iold, iyoung, ccommands, pazcommands,
829	     fnotcommands, zcomment, cstdin)
830     pointer puuconf;
831     int icmd;
832     int csystems;
833     char **pazsystems;
834     boolean fnotsystems;
835     int cusers;
836     char **pazusers;
837     boolean fnotusers;
838     long iold;
839     long iyoung;
840     int ccommands;
841     char **pazcommands;
842     boolean fnotcommands;
843     const char *zcomment;
844     int cstdin;
845{
846  boolean fret;
847  int i;
848  int iuuconf;
849  struct uuconf_system ssys;
850
851  fret = TRUE;
852
853  if (csystems > 0 && ! fnotsystems)
854    {
855      for (i = 0; i < csystems; i++)
856	{
857	  iuuconf = uuconf_system_info (puuconf, pazsystems[i], &ssys);
858	  if (iuuconf != UUCONF_SUCCESS)
859	    {
860	      if (iuuconf == UUCONF_NOT_FOUND)
861		ulog (LOG_ERROR, "%s: System not found", pazsystems[i]);
862	      else
863		ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
864	      fret = FALSE;
865	      continue;
866	    }
867
868	  if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers,
869				    fnotusers, iold, iyoung, ccommands,
870				    pazcommands, fnotcommands, zcomment,
871				    cstdin))
872	    fret = FALSE;
873
874	  (void) uuconf_system_free (puuconf, &ssys);
875	}
876    }
877  else
878    {
879      char **pznames, **pz;
880
881      iuuconf = uuconf_system_names (puuconf, &pznames, 0);
882      if (iuuconf != UUCONF_SUCCESS)
883	{
884	  ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
885	  return FALSE;
886	}
887
888      for (pz = pznames; *pz != NULL; pz++)
889	{
890	  if (csystems > 0)
891	    {
892	      for (i = 0; i < csystems; i++)
893		if (strcmp (*pz, pazsystems[i]) == 0)
894		  break;
895	      if (i < csystems)
896		continue;
897	    }
898
899	  iuuconf = uuconf_system_info (puuconf, *pz, &ssys);
900	  if (iuuconf != UUCONF_SUCCESS)
901	    {
902	      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
903	      fret = FALSE;
904	      continue;
905	    }
906
907	  if (! fsworkfiles_system (puuconf, icmd, &ssys, cusers, pazusers,
908				    fnotusers, iold, iyoung, ccommands,
909				    pazcommands, fnotcommands, zcomment,
910				    cstdin))
911	    fret = FALSE;
912
913	  (void) uuconf_system_free (puuconf, &ssys);
914	  xfree ((pointer) *pz);
915	}
916      xfree ((pointer) pznames);
917    }
918
919  return fret;
920}
921
922/* Look at the work files for a particular system.  */
923
924static boolean
925fsworkfiles_system (puuconf, icmd, qsys, cusers, pazusers, fnotusers, iold,
926		    iyoung, ccommands, pazcommands, fnotcommands, zcomment,
927		    cstdin)
928     pointer puuconf;
929     int icmd;
930     const struct uuconf_system *qsys;
931     int cusers;
932     char **pazusers;
933     boolean fnotusers;
934     long iold;
935     long iyoung;
936     int ccommands;
937     char **pazcommands;
938     boolean fnotcommands;
939     const char *zcomment;
940     int cstdin;
941{
942  boolean fret;
943
944  if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW, 0))
945    return FALSE;
946
947  while (TRUE)
948    {
949      struct scmd s;
950      long itime;
951
952      if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, 0, &s))
953	{
954	  usysdep_get_work_free (qsys);
955	  return FALSE;
956	}
957      if (s.bcmd == 'H')
958	break;
959
960      if (cusers > 0)
961	{
962	  boolean fmatch;
963	  int i;
964
965	  fmatch = fnotusers;
966	  for (i = 0; i < cusers; i++)
967	    {
968	      if (s.zuser != NULL
969		  && strcmp (pazusers[i], s.zuser) == 0)
970		{
971		  fmatch = ! fmatch;
972		  break;
973		}
974	    }
975	  if (! fmatch)
976	    continue;
977	}
978
979      itime = ixsysdep_work_time (qsys, s.pseq);
980
981      if (iold != (long) -1 && itime > iold)
982	continue;
983
984      if (iyoung != (long) -1 && itime < iyoung)
985	continue;
986
987      if (! fsworkfile_show (puuconf, icmd, qsys, &s, itime, ccommands,
988			     pazcommands, fnotcommands, zcomment, cstdin))
989	{
990	  usysdep_get_work_free (qsys);
991	  return FALSE;
992	}
993    }
994
995  fret = fsworkfile_show (puuconf, icmd, qsys, (const struct scmd *) NULL,
996			  0L, ccommands, pazcommands, fnotcommands, zcomment,
997			  cstdin);
998
999  usysdep_get_work_free (qsys);
1000
1001  return fret;
1002}
1003
1004/* Show a single workfile.  This is actually called once for each line
1005   in the workfile, so we accumulate the lines and show them all at
1006   once.  This lets us show an execution in a useful fashion.  */
1007
1008static boolean
1009fsworkfile_show (puuconf, icmd, qsys, qcmd, itime, ccommands, pazcommands,
1010		 fnotcommands, zcomment, cstdin)
1011     pointer puuconf;
1012     int icmd;
1013     const struct uuconf_system *qsys;
1014     const struct scmd *qcmd;
1015     long itime;
1016     int ccommands;
1017     char **pazcommands;
1018     boolean fnotcommands;
1019     const char *zcomment;
1020     int cstdin;
1021{
1022  static struct scmdlist *qlist;
1023  static char *zlistid;
1024  char *zid;
1025
1026  if (qcmd == NULL)
1027    zid = NULL;
1028  else
1029    {
1030      zid = zsysdep_jobid (qsys, qcmd->pseq);
1031      if (zid == NULL)
1032	return FALSE;
1033    }
1034
1035  /* If this is the same jobid as the list, put it on the end.  */
1036
1037  if (qcmd != NULL
1038      && qlist != NULL
1039      && strcmp (zlistid, zid) == 0)
1040    {
1041      struct scmdlist *qnew, **pq;
1042
1043      ubuffree (zid);
1044      qnew = (struct scmdlist *) xmalloc (sizeof (struct scmdlist));
1045      qnew->qnext = NULL;
1046      qnew->s = *qcmd;
1047      qnew->itime = itime;
1048      for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext)
1049	;
1050      *pq = qnew;
1051      return TRUE;
1052    }
1053
1054  /* Here we have found a different job ID, so we print the scmd
1055     structures that we have accumulated.  We look for the special
1056     case of an execution (an E command, or one of the destination
1057     files begins with X.).  We could be more clever about other
1058     situations as well.  */
1059  if (qlist != NULL)
1060    {
1061      boolean fmatch;
1062      const char *zprog, *zcmd, *zrequestor, *zstdin;
1063      char *zfree;
1064      struct scmdlist *qxqt;
1065      FILE *exqt = NULL;
1066      struct scmdlist *qfree;
1067
1068      fmatch = FALSE;
1069      zprog = zcmd = zrequestor = zstdin = NULL;
1070      zfree = NULL;
1071
1072      for (qxqt = qlist; qxqt != NULL; qxqt = qxqt->qnext)
1073	{
1074	  if (qxqt->s.bcmd == 'E')
1075	    break;
1076	  if (qxqt->s.bcmd == 'S'
1077	      && qxqt->s.zto[0] == 'X'
1078	      && qxqt->s.zto[1] == '.'
1079	      && fspool_file (qxqt->s.zfrom))
1080	    {
1081	      char *zxqt;
1082
1083	      /* Open the file now, so that, if it does not exist, we
1084                 can still report sensibly (the qxqt == NULL case) on
1085                 any other files that may exist.  */
1086
1087	      zxqt = zsysdep_spool_file_name (qsys, qxqt->s.zfrom,
1088					      qxqt->s.pseq);
1089	      if (zxqt == NULL)
1090		return FALSE;
1091
1092	      exqt = fopen (zxqt, "r");
1093
1094	      ubuffree (zxqt);
1095
1096	      if (exqt != NULL)
1097		break;
1098	    }
1099	}
1100
1101      if (qxqt == NULL)
1102	{
1103	  if (ccommands == 0
1104	      || (fnotcommands
1105		  && strcmp (pazcommands[0], "ALL") == 0))
1106	    {
1107	      /* Show all the lines in a regular work file.  */
1108	      fmatch = TRUE;
1109
1110	      if ((icmd & JOB_SHOW) != 0)
1111		{
1112		  struct scmdlist *qshow;
1113
1114		  for (qshow = qlist; qshow != NULL; qshow = qshow->qnext)
1115		    {
1116		      char *zfile;
1117		      long cbytes;
1118
1119		      usworkfile_header (qsys, &qshow->s, zlistid,
1120					 qshow->itime, qshow == qlist);
1121
1122		      switch (qshow->s.bcmd)
1123			{
1124			case 'S':
1125			  if (strchr (qshow->s.zoptions, 'C') != NULL
1126			      || fspool_file (qshow->s.zfrom))
1127			    zfile = zsysdep_spool_file_name (qsys,
1128							     qshow->s.ztemp,
1129							     qshow->s.pseq);
1130			  else
1131			    zfile = zbufcpy (qshow->s.zfrom);
1132			  if (zfile == NULL)
1133			    cbytes = -1;
1134			  else
1135			    cbytes = csysdep_size (zfile);
1136			  if (cbytes >= 0)
1137			    printf ("Sending %s (%ld bytes) to %s",
1138				    qshow->s.zfrom, cbytes, qshow->s.zto);
1139			  ubuffree (zfile);
1140			  break;
1141			case 'R':
1142			  printf ("Requesting %s to %s", qshow->s.zfrom,
1143				  qshow->s.zto);
1144			  break;
1145			case 'X':
1146			  printf ("Requesting %s to %s", qshow->s.zfrom,
1147				  qshow->s.zto);
1148			  break;
1149			case 'P':
1150			  printf ("(poll file)");
1151			  break;
1152#if DEBUG > 0
1153			default:
1154			  printf ("Bad line %d", qshow->s.bcmd);
1155			  break;
1156#endif
1157			}
1158
1159		      printf ("\n");
1160		    }
1161		}
1162	    }
1163	}
1164      else
1165	{
1166	  long csize;
1167	  struct scmdlist *qsize;
1168
1169	  /* Show the command for an execution file.  */
1170	  if (qxqt->s.bcmd == 'E')
1171	    {
1172	      zfree = zbufcpy (qxqt->s.zcmd);
1173	      zfree[strcspn (zfree, " \t")] = '\0';
1174	      zprog = zfree;
1175	      zcmd = qxqt->s.zcmd;
1176	      if (strchr (qxqt->s.zoptions, 'R') != NULL)
1177		zrequestor = qxqt->s.znotify;
1178	    }
1179	  else
1180	    {
1181	      if (! fsxqt_file_read (puuconf, exqt))
1182		{
1183		  (void) fclose (exqt);
1184		  return FALSE;
1185		}
1186
1187	      (void) fclose (exqt);
1188
1189	      zprog = zSxqt_prog;
1190	      zcmd = zSxqt_cmd;
1191	      zrequestor = zSxqt_requestor;
1192	    }
1193
1194	  csize = 0L;
1195	  for (qsize = qlist; qsize != NULL; qsize = qsize->qnext)
1196	    {
1197	      if (qsize->s.bcmd == 'S' || qsize->s.bcmd == 'E')
1198		{
1199		  char *zfile;
1200
1201		  if (strchr (qsize->s.zoptions, 'C') != NULL
1202		      || fspool_file (qsize->s.zfrom))
1203		    zfile = zsysdep_spool_file_name (qsys, qsize->s.ztemp,
1204						     qsize->s.pseq);
1205		  else
1206		    zfile = zbufcpy (qsize->s.zfrom);
1207		  if (zfile != NULL)
1208		    {
1209		      long cbytes;
1210
1211		      cbytes = csysdep_size (zfile);
1212		      if (cbytes > 0)
1213			csize += cbytes;
1214		      ubuffree (zfile);
1215		    }
1216		}
1217	    }
1218
1219	  if (ccommands == 0)
1220	    fmatch = TRUE;
1221	  else
1222	    {
1223	      int i;
1224
1225	      fmatch = fnotcommands;
1226	      for (i = 0; i < ccommands; i++)
1227		{
1228		  if (strcmp (pazcommands[i], "ALL") == 0
1229		      || strcmp (pazcommands[i], zprog) == 0)
1230		    {
1231		      fmatch = ! fmatch;
1232		      break;
1233		    }
1234		}
1235	    }
1236
1237	  /* To get the name of the standard input file on this system
1238	     we have to look through the list of file transfers to
1239	     find the right one on the remote system.  */
1240	  if (fmatch)
1241	    {
1242	      struct scmdlist *qstdin;
1243
1244	      if (qxqt->s.bcmd == 'E')
1245		qstdin = qxqt;
1246	      else if (zSxqt_stdin != NULL)
1247		{
1248		  for (qstdin = qlist;
1249		       qstdin != NULL;
1250		       qstdin = qstdin->qnext)
1251		    if (qstdin->s.bcmd == 'S'
1252			&& strcmp (qstdin->s.zto, zSxqt_stdin) == 0)
1253		      break;
1254		}
1255	      else
1256		qstdin = NULL;
1257
1258	      if (qstdin != NULL)
1259		{
1260		  if (strchr (qstdin->s.zoptions, 'C') != NULL
1261		      || fspool_file (qstdin->s.zfrom))
1262		    zstdin = qstdin->s.ztemp;
1263		  else
1264		    zstdin = qstdin->s.zfrom;
1265		}
1266	    }
1267
1268	  if (fmatch && (icmd & JOB_SHOW) != 0)
1269	    {
1270	      usworkfile_header (qsys, &qxqt->s, zlistid, qxqt->itime,
1271				 TRUE);
1272	      printf ("Executing %s (sending %ld bytes)\n", zcmd, csize);
1273	    }
1274	}
1275
1276      if (fmatch)
1277	{
1278	  boolean fkill_or_rejuv;
1279
1280	  fkill_or_rejuv = FALSE;
1281	  if ((icmd & JOB_INQUIRE) != 0)
1282	    {
1283	      int b;
1284
1285	      /* Ask stdin whether this job should be killed.  */
1286	      fprintf (stderr, "%s: %s %s? ",
1287		       zProgram,
1288		       (icmd & JOB_REJUVENATE) != 0 ? "Rejuvenate" : "Kill",
1289		       zlistid);
1290	      (void) fflush (stderr);
1291	      b = getchar ();
1292	      fkill_or_rejuv = b == 'y' || b == 'Y';
1293	      while (b != EOF && b != '\n')
1294		b = getchar ();
1295	    }
1296	  else if ((icmd & JOB_KILL) != 0
1297		   || (icmd & JOB_REJUVENATE) != 0)
1298	    fkill_or_rejuv = TRUE;
1299
1300	  if (fkill_or_rejuv
1301	      && (qlist->s.zuser == NULL
1302		  || strcmp (zsysdep_login_name (), qlist->s.zuser) != 0)
1303	      && ! fsysdep_privileged ())
1304	    ulog (LOG_ERROR, "%s: Not submitted by you", zlistid);
1305	  else
1306	    {
1307	      if ((icmd & (JOB_MAIL | JOB_NOTIFY)) != 0)
1308		{
1309		  if (! fsnotify (puuconf, icmd, zcomment, cstdin,
1310				  (fkill_or_rejuv &&
1311				   (icmd & JOB_REJUVENATE) == 0),
1312				  zcmd, qlist, zlistid, qlist->itime,
1313				  qlist->s.zuser, qsys, zstdin,
1314				  qlist->s.pseq, zrequestor))
1315		    return FALSE;
1316		}
1317
1318	      if (fkill_or_rejuv)
1319		{
1320		  if ((icmd & JOB_REJUVENATE) == 0)
1321		    {
1322		      if (! fsysdep_kill_job (puuconf, zlistid))
1323			return FALSE;
1324		    }
1325		  else
1326		    {
1327		      if (! fsysdep_rejuvenate_job (puuconf, zlistid))
1328			return FALSE;
1329		    }
1330		}
1331	    }
1332	}
1333
1334      if (qxqt != NULL)
1335	{
1336	  if (qxqt->s.bcmd == 'E')
1337	    ubuffree (zfree);
1338	  else
1339	    usxqt_file_free ();
1340	}
1341
1342      /* Free up the list of entries.  */
1343      qfree = qlist;
1344      while (qfree != NULL)
1345	{
1346	  struct scmdlist *qnext;
1347
1348	  qnext = qfree->qnext;
1349	  xfree ((pointer) qfree);
1350	  qfree = qnext;
1351	}
1352
1353      ubuffree (zlistid);
1354
1355      qlist = NULL;
1356      zlistid = NULL;
1357    }
1358
1359  /* Start a new list with the entry we just got.  */
1360  if (qcmd != NULL)
1361    {
1362      qlist = (struct scmdlist *) xmalloc (sizeof (struct scmdlist));
1363      qlist->qnext = NULL;
1364      qlist->s = *qcmd;
1365      qlist->itime = itime;
1366      zlistid = zid;
1367    }
1368
1369  return TRUE;
1370}
1371
1372/* Show the header of the line describing a workfile.  */
1373
1374static void
1375usworkfile_header (qsys, qcmd, zjobid, itime, ffirst)
1376     const struct uuconf_system *qsys;
1377     const struct scmd *qcmd;
1378     const char *zjobid;
1379     long itime;
1380     boolean ffirst;
1381{
1382  const char *zshowid;
1383  struct tm stime;
1384
1385  if (ffirst)
1386    zshowid = zjobid;
1387  else
1388    zshowid = "-";
1389
1390  printf ("%s %s %s ", zshowid, qsys->uuconf_zname,
1391	  qcmd->zuser != NULL ? qcmd->zuser : OWNER);
1392
1393  usysdep_localtime (itime, &stime);
1394  printf ("%02d-%02d %02d:%02d ",
1395	  stime.tm_mon + 1, stime.tm_mday, stime.tm_hour, stime.tm_min);
1396}
1397
1398/* List queued executions that have not been processed by uuxqt for
1399   one reason or another.  */
1400
1401static boolean
1402fsexecutions (puuconf, icmd, csystems, pazsystems, fnotsystems, cusers,
1403	      pazusers, fnotusers, iold, iyoung, ccommands, pazcommands,
1404	      fnotcommands, zcomment, cstdin)
1405     pointer puuconf;
1406     int icmd;
1407     int csystems;
1408     char **pazsystems;
1409     boolean fnotsystems;
1410     int cusers;
1411     char **pazusers;
1412     boolean fnotusers;
1413     long iold;
1414     long iyoung;
1415     int ccommands;
1416     char **pazcommands;
1417     boolean fnotcommands;
1418     const char *zcomment;
1419     int cstdin;
1420{
1421  const char *zlocalname;
1422  int iuuconf;
1423  char *zfile;
1424  char *zsystem;
1425  boolean ferr;
1426
1427  iuuconf = uuconf_localname (puuconf, &zlocalname);
1428  if (iuuconf == UUCONF_NOT_FOUND)
1429    {
1430      zlocalname = zsysdep_localname ();
1431      if (zlocalname == NULL)
1432	return FALSE;
1433    }
1434  else if (iuuconf != UUCONF_SUCCESS)
1435    {
1436      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1437      return FALSE;
1438    }
1439
1440  if (! fsysdep_get_xqt_init ((const char *) NULL))
1441    return FALSE;
1442
1443  while ((zfile = zsysdep_get_xqt ((const char *) NULL, &zsystem, &ferr))
1444	 != NULL)
1445    {
1446      boolean fmatch;
1447      int i;
1448      long itime;
1449      FILE *e;
1450
1451      if (csystems > 0)
1452	{
1453	  fmatch = fnotsystems;
1454	  for (i = 0; i < csystems; i++)
1455	    {
1456	      if (strcmp (pazsystems[i], zsystem) == 0)
1457		{
1458		  fmatch = ! fmatch;
1459		  break;
1460		}
1461	    }
1462	  if (! fmatch)
1463	    {
1464	      ubuffree (zfile);
1465	      ubuffree (zsystem);
1466	      continue;
1467	    }
1468	}
1469
1470      itime = ixsysdep_file_time (zfile);
1471
1472      if ((iold != (long) -1 && itime > iold)
1473	  || (iyoung != (long) -1 && itime < iyoung))
1474	{
1475	  ubuffree (zfile);
1476	  ubuffree (zsystem);
1477	  continue;
1478	}
1479
1480      /* We need to read the execution file before we can check the
1481	 user name.  */
1482      e = fopen (zfile, "r");
1483      if (e == NULL)
1484	{
1485	  /* Probably uucico just deleted the file.  */
1486	  continue;
1487	}
1488      if (! fsxqt_file_read (puuconf, e))
1489	{
1490	  (void) fclose (e);
1491	  ubuffree (zfile);
1492	  ubuffree (zsystem);
1493	  continue;
1494	}
1495      (void) fclose (e);
1496
1497      if (cusers == 0)
1498	fmatch = TRUE;
1499      else
1500	{
1501	  fmatch = fnotusers;
1502	  for (i = 0; i < cusers; i++)
1503	    {
1504	      if (strcmp (zSxqt_user, pazusers[i]) == 0
1505		  || (zSxqt_requestor != NULL
1506		      && strcmp (zSxqt_requestor, pazusers[i]) == 0))
1507		{
1508		  fmatch = ! fmatch;
1509		  break;
1510		}
1511	    }
1512	}
1513
1514      if (fmatch && ccommands > 0)
1515	{
1516	  fmatch = fnotcommands;
1517	  for (i = 0; i < ccommands; i++)
1518	    {
1519	      if (strcmp (pazcommands[i], "ALL") == 0
1520		  || strcmp (pazcommands[i], zSxqt_prog) == 0)
1521		{
1522		  fmatch = ! fmatch;
1523		  break;
1524		}
1525	    }
1526	}
1527
1528      if (fmatch)
1529	{
1530	  boolean fbad, fkill_or_rejuv;
1531	  struct uuconf_system ssys;
1532
1533	  fbad = FALSE;
1534
1535	  if ((icmd & JOB_SHOW) != 0)
1536	    {
1537	      struct tm stime;
1538
1539	      printf ("%s %s!", zsystem, zSxqt_system);
1540	      if (zSxqt_requestor != NULL)
1541		printf ("%s", zSxqt_requestor);
1542	      else
1543		printf ("%s", zSxqt_user);
1544
1545	      usysdep_localtime (itime, &stime);
1546	      printf (" %02d-%02d %02d:%02d ",
1547		      stime.tm_mon + 1, stime.tm_mday, stime.tm_hour,
1548		      stime.tm_min);
1549
1550	      printf ("%s\n", zSxqt_cmd);
1551	    }
1552
1553	  fkill_or_rejuv = FALSE;
1554	  if ((icmd & JOB_INQUIRE) != 0)
1555	    {
1556	      int b;
1557
1558	      /* Ask stdin whether this job should be killed.  */
1559	      fprintf (stderr, "%s: %s %s? ",
1560		       zProgram,
1561		       (icmd & JOB_REJUVENATE) != 0 ? "Rejuvenate" : "Kill",
1562		       zSxqt_cmd);
1563	      (void) fflush (stderr);
1564	      b = getchar ();
1565	      fkill_or_rejuv = b == 'y' || b == 'Y';
1566	      while (b != EOF && b != '\n')
1567		b = getchar ();
1568	    }
1569	  else if ((icmd & JOB_KILL) != 0
1570		   || (icmd & JOB_REJUVENATE) != 0)
1571	    fkill_or_rejuv = TRUE;
1572
1573	  if (fkill_or_rejuv)
1574	    {
1575	      if ((strcmp (zSxqt_user, zsysdep_login_name ()) != 0
1576		   || strcmp (zsystem, zlocalname) != 0)
1577		  && ! fsysdep_privileged ())
1578		{
1579		  ulog (LOG_ERROR, "Job not submitted by you\n");
1580		  fbad = TRUE;
1581		}
1582	    }
1583
1584	  if (! fbad)
1585	    {
1586	      iuuconf = uuconf_system_info (puuconf, zsystem, &ssys);
1587	      if (iuuconf != UUCONF_SUCCESS)
1588		{
1589		  if (iuuconf != UUCONF_NOT_FOUND)
1590		    {
1591		      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1592		      fbad = TRUE;
1593		    }
1594		  else if (strcmp (zsystem, zlocalname) == 0)
1595		    {
1596		      iuuconf = uuconf_system_local (puuconf, &ssys);
1597		      if (iuuconf != UUCONF_SUCCESS)
1598			{
1599			  ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1600			  fbad = TRUE;
1601			}
1602		      ssys.uuconf_zname = (char *) zlocalname;
1603		    }
1604		  else if (! funknown_system (puuconf, zsystem, &ssys))
1605		    {
1606		      ulog (LOG_ERROR, "Job for unknown system %s",
1607			    zsystem);
1608		      fbad = TRUE;
1609		    }
1610		}
1611	    }
1612
1613	  if (! fbad && (icmd & (JOB_MAIL | JOB_NOTIFY)) != 0)
1614	    {
1615	      if (! fsnotify (puuconf, icmd, zcomment, cstdin,
1616			      fkill_or_rejuv && (icmd & JOB_REJUVENATE) == 0,
1617			      zSxqt_cmd, (struct scmdlist *) NULL,
1618			      (const char *) NULL, itime, zSxqt_user, &ssys,
1619			      zSxqt_stdin, (pointer) NULL, zSxqt_requestor))
1620		{
1621		  ferr = TRUE;
1622		  usxqt_file_free ();
1623		  ubuffree (zfile);
1624		  ubuffree (zsystem);
1625		  break;
1626		}
1627	    }
1628
1629	  if (! fbad && fkill_or_rejuv)
1630	    {
1631	      for (i = 0; i < cSxqt_files; i++)
1632		{
1633		  char *z;
1634
1635		  z = zsysdep_spool_file_name (&ssys, pazSxqt_files[i],
1636					       (pointer) NULL);
1637		  if (z != NULL)
1638		    {
1639		      if ((icmd & JOB_REJUVENATE) != 0)
1640			(void) fsysdep_touch_file (z);
1641		      else
1642			(void) remove (z);
1643		      ubuffree (z);
1644		    }
1645		}
1646	      if ((icmd & JOB_REJUVENATE) != 0)
1647		(void) fsysdep_touch_file (zfile);
1648	      else
1649		{
1650		  if (remove (zfile) != 0)
1651		    ulog (LOG_ERROR, "remove (%s): %s", zfile,
1652			  strerror (errno));
1653		}
1654	    }
1655
1656	  if (! fbad)
1657	    (void) uuconf_system_free (puuconf, &ssys);
1658	}
1659
1660      usxqt_file_free ();
1661      ubuffree (zfile);
1662      ubuffree (zsystem);
1663    }
1664
1665  usysdep_get_xqt_free ((const char *) NULL);
1666
1667  return ferr;
1668}
1669
1670/* When a job is killed, send mail to the appropriate people.  */
1671
1672static boolean
1673fsnotify (puuconf, icmd, zcomment, cstdin, fkilled, zcmd, qcmd, zid, itime,
1674	  zuser, qsys, zstdin, pstdinseq, zrequestor)
1675     pointer puuconf;
1676     int icmd;
1677     const char *zcomment;
1678     int cstdin;
1679     boolean fkilled;
1680     const char *zcmd;
1681     struct scmdlist *qcmd;
1682     const char *zid;
1683     long itime;
1684     const char *zuser;
1685     const struct uuconf_system *qsys;
1686     const char *zstdin;
1687     pointer pstdinseq;
1688     const char *zrequestor;
1689{
1690  const char **pz;
1691  int cgot;
1692  int i, istdin;
1693  struct tm stime;
1694  char ab[sizeof "1991-12-31 12:00:00"];
1695  const char *zsubject;
1696  boolean fret;
1697
1698  pz = (const char **) xmalloc (20 * sizeof (const char *));
1699  cgot = 20;
1700
1701  i = 0;
1702  if (zid == NULL)
1703    pz[i++] = "A UUCP execution request";
1704  else
1705    {
1706      pz[i++] = "UUCP job\n\t";
1707      pz[i++] = zid;
1708      pz[i++] = "\nfor system\n\t";
1709      pz[i++] = qsys->uuconf_zname;
1710    }
1711  pz[i++] = "\nrequested by\n\t";
1712  pz[i++] = zuser != NULL ? zuser : OWNER;
1713  if (zid == NULL)
1714    {
1715      pz[i++] = "\non system\n\t";
1716      pz[i++] = qsys->uuconf_zname;
1717    }
1718  pz[i++] = "\n";
1719
1720  if (fkilled)
1721    pz[i++] = "has been killed.\n";
1722
1723  if (zcomment != NULL)
1724    {
1725      pz[i++] = zcomment;
1726      pz[i++] = "\n";
1727    }
1728
1729  pz[i++] = "The job was queued at ";
1730  usysdep_localtime (itime, &stime);
1731  sprintf (ab, "%04d-%02d-%02d %02d:%02d:%02d",
1732	   stime.tm_year + 1900, stime.tm_mon + 1, stime.tm_mday,
1733	   stime.tm_hour, stime.tm_min, stime.tm_sec);
1734  pz[i++] = ab;
1735  pz[i++] = ".\nIt ";
1736
1737  if (fkilled)
1738    pz[i++] = "was\n";
1739  else
1740    pz[i++] = "is\n";
1741
1742  if (zcmd != NULL)
1743    {
1744      pz[i++] = "\t";
1745      pz[i++] = zcmd;
1746    }
1747  else
1748    {
1749      struct scmdlist *qshow;
1750
1751      for (qshow = qcmd; qshow != NULL; qshow = qshow->qnext)
1752	{
1753	  if (i + 10 > cgot)
1754	    {
1755	      cgot += 20;
1756	      pz = (const char **) xrealloc ((pointer) pz,
1757					     cgot * sizeof (const char *));
1758	    }
1759
1760	  switch (qshow->s.bcmd)
1761	    {
1762	    case 'S':
1763	      pz[i++] = "\tsend ";
1764	      break;
1765	    default:
1766	    case 'R':
1767	    case 'X':
1768	      pz[i++] = "\trequest ";
1769	      break;
1770	    case 'P':
1771	      pz[i++] = "\tpoll ";
1772	      break;
1773#if DEBUG > 0
1774	    case 'E':
1775	      ulog (LOG_FATAL, "fsnotify: Can't happen");
1776	      break;
1777#endif
1778	    }
1779	  if (qshow->s.zfrom != NULL && qshow->s.zto != NULL)
1780	    {
1781	      pz[i++] = qshow->s.zfrom;
1782	      pz[i++] = " to ";
1783	      pz[i++] = qshow->s.zto;
1784	    }
1785	}
1786    }
1787
1788  istdin = i;
1789  if (cstdin > 0 && zstdin != NULL)
1790    {
1791      boolean fspool;
1792      char *zfile;
1793      FILE *e;
1794
1795      fspool = fspool_file (zstdin);
1796      if (fspool)
1797	zfile = zsysdep_spool_file_name (qsys, zstdin, pstdinseq);
1798      else
1799	zfile = zsysdep_local_file (zstdin, qsys->uuconf_zpubdir,
1800				    (boolean *) NULL);
1801
1802      if (zfile != NULL
1803	  && (fspool
1804	      || fin_directory_list (zfile, qsys->uuconf_pzremote_send,
1805				     qsys->uuconf_zpubdir, TRUE, TRUE,
1806				     (const char *) NULL)))
1807	{
1808	  e = fopen (zfile, "r");
1809	  if (e != NULL)
1810	    {
1811	      int clines, clen;
1812	      char *zline;
1813	      size_t cline;
1814
1815	      pz[i++] = "\n";
1816	      istdin = i;
1817
1818	      clines = 0;
1819
1820	      zline = NULL;
1821	      cline = 0;
1822	      while ((clen = getline (&zline, &cline, e)) > 0)
1823		{
1824		  if (memchr (zline, '\0', (size_t) clen) != NULL)
1825		    {
1826		      int ifree;
1827
1828		      /* A null character means this is probably a
1829			 binary file.  */
1830		      for (ifree = istdin; ifree < i; ifree++)
1831			ubuffree ((char *) pz[ifree]);
1832		      i = istdin - 1;
1833		      break;
1834		    }
1835		  ++clines;
1836		  if (clines > cstdin)
1837		    break;
1838		  if (i >= cgot)
1839		    {
1840		      cgot += 20;
1841		      pz = (const char **) xrealloc ((pointer) pz,
1842						     (cgot
1843						      * sizeof (char *)));
1844		    }
1845		  if (strncmp (zline, "From ", sizeof "From " - 1) != 0)
1846		    pz[i++] = zbufcpy (zline);
1847		  else
1848		    {
1849		      char *zalc;
1850
1851		      /* Escape "From " at the start of a line.  This
1852			 should really be the responsibility of the
1853			 mail transfer agent.  On some systems,
1854			 though, the mail transfer agent does not do
1855			 it, but user mail programs expect it.  We
1856			 help them out here, since it doesn't matter
1857			 much--we're already truncating the message
1858			 anyhow.  */
1859		      zalc = zbufalc (strlen (zline) + 2);
1860		      zalc[0] = '>';
1861		      strcpy (zalc + 1, zline);
1862		      pz[i++] = zalc;
1863		    }
1864		}
1865	      xfree ((pointer) zline);
1866	      (void) fclose (e);
1867	    }
1868	}
1869
1870      ubuffree (zfile);
1871    }
1872
1873  if (fkilled)
1874    zsubject = "UUCP job killed";
1875  else
1876    zsubject = "UUCP notification";
1877
1878  fret = TRUE;
1879
1880  if ((icmd & JOB_MAIL) != 0)
1881    {
1882      if (! fsysdep_mail (OWNER, zsubject, i, pz))
1883	fret = FALSE;
1884    }
1885
1886  if ((icmd & JOB_NOTIFY) != 0
1887      && (zrequestor != NULL || zuser != NULL))
1888    {
1889      const char *zmail;
1890      char *zfree;
1891
1892      if (zrequestor != NULL)
1893	zmail = zrequestor;
1894      else
1895	zmail = zuser;
1896
1897      zfree = NULL;
1898
1899      if (zid == NULL)
1900	{
1901	  int iuuconf;
1902	  const char *zloc;
1903
1904	  /* This is an execution request, which may be from another
1905	     system.  If it is, we must prepend that system name to
1906	     the user name extracted from the X. file.  */
1907	  iuuconf = uuconf_localname (puuconf, &zloc);
1908	  if (iuuconf == UUCONF_NOT_FOUND)
1909	    {
1910	      zloc = zsysdep_localname ();
1911	      if (zloc == NULL)
1912		return FALSE;
1913	    }
1914	  else if (iuuconf != UUCONF_SUCCESS)
1915	    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
1916
1917	  if (strcmp (qsys->uuconf_zname, zloc) != 0
1918#if HAVE_INTERNET_MAIL
1919	      && strchr (zmail, '@') == NULL
1920#endif
1921	      )
1922	    {
1923	      zfree = zbufalc (strlen (qsys->uuconf_zname)
1924			       + strlen (zmail)
1925			       + sizeof "!");
1926	      sprintf (zfree, "%s!%s", qsys->uuconf_zname, zmail);
1927	      zmail = zfree;
1928	    }
1929	}
1930
1931      if (! fsysdep_mail (zmail, zsubject, i, pz))
1932	fret = FALSE;
1933
1934      ubuffree (zfree);
1935    }
1936
1937  while (istdin < i)
1938    {
1939      ubuffree ((char *) pz[istdin]);
1940      istdin++;
1941    }
1942
1943  xfree ((pointer) pz);
1944
1945  return fret;
1946}
1947
1948/* Handle the -q option.  For each remote system this lists the number
1949   of jobs queued, the number of executions queued, and the current
1950   call status.  We get the executions all at once, because they are
1951   not accessed by system.  They could be, but it is possible to have
1952   executions pending for an unknown system, so special handling would
1953   still be required.  */
1954
1955struct sxqtlist
1956{
1957  struct sxqtlist *qnext;
1958  char *zsystem;
1959  int cxqts;
1960  long ifirst;
1961};
1962
1963/* These local functions need the definition of sxqtlist for the
1964   prototype.  */
1965
1966static boolean fsquery_system P((const struct uuconf_system *qsys,
1967				 struct sxqtlist **pq,
1968				 long inow, const char *zlocalname,
1969				 int csystems, char **pazsystems,
1970				 boolean fnotsystems, long iold, long iyoung));
1971static boolean fsquery_show P((const struct uuconf_system *qsys, int cwork,
1972			       long ifirstwork, struct sxqtlist *qxqt,
1973			       long inow, const char *zlocalname,
1974			       int csystems, char **pazsystems,
1975			       boolean fnotsystems, long iold, long iyoung));
1976
1977static boolean
1978fsquery (puuconf, csystems, pazsystems, fnotsystems, iold, iyoung)
1979     pointer puuconf;
1980     int csystems;
1981     char **pazsystems;
1982     boolean fnotsystems;
1983     long iold;
1984     long iyoung;
1985{
1986  int iuuconf;
1987  const char *zlocalname;
1988  struct sxqtlist *qlist;
1989  char *zfile, *zsystem;
1990  boolean ferr;
1991  long inow;
1992  char **pznames, **pz;
1993  boolean fret;
1994
1995  iuuconf = uuconf_localname (puuconf, &zlocalname);
1996  if (iuuconf == UUCONF_NOT_FOUND)
1997    {
1998      zlocalname = zsysdep_localname ();
1999      if (zlocalname == NULL)
2000	return FALSE;
2001    }
2002  else if (iuuconf != UUCONF_SUCCESS)
2003    {
2004      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
2005      return FALSE;
2006    }
2007
2008  /* Get a count of all the execution files.  */
2009  if (! fsysdep_get_xqt_init ((const char *) NULL))
2010    return FALSE;
2011
2012  qlist = NULL;
2013  while ((zfile = zsysdep_get_xqt ((const char *) NULL, &zsystem, &ferr))
2014	 != NULL)
2015    {
2016      struct sxqtlist *qlook;
2017
2018      for (qlook = qlist; qlook != NULL; qlook = qlook->qnext)
2019	if (strcmp (zsystem, qlook->zsystem) == 0)
2020	  break;
2021
2022      if (qlook != NULL)
2023	{
2024	  long itime;
2025
2026	  ubuffree (zsystem);
2027	  ++qlook->cxqts;
2028	  itime = ixsysdep_file_time (zfile);
2029	  if (itime < qlook->ifirst)
2030	    qlook->ifirst = itime;
2031	}
2032      else
2033	{
2034	  struct sxqtlist *qnew;
2035
2036	  qnew = (struct sxqtlist *) xmalloc (sizeof (struct sxqtlist));
2037	  qnew->qnext = qlist;
2038	  qnew->zsystem = zsystem;
2039	  qnew->cxqts = 1;
2040	  qnew->ifirst = ixsysdep_file_time (zfile);
2041	  qlist = qnew;
2042	}
2043
2044      ubuffree (zfile);
2045    }
2046
2047  usysdep_get_xqt_free ((const char *) NULL);
2048
2049  if (ferr)
2050    return FALSE;
2051
2052  inow = ixsysdep_time ((long *) NULL);
2053
2054  /* Show the information for each system.  */
2055  iuuconf = uuconf_system_names (puuconf, &pznames, 0);
2056  if (iuuconf != UUCONF_SUCCESS)
2057    {
2058      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
2059      return FALSE;
2060    }
2061
2062  fret = TRUE;
2063
2064  for (pz = pznames; *pz != NULL; pz++)
2065    {
2066      struct uuconf_system ssys;
2067
2068      iuuconf = uuconf_system_info (puuconf, *pz, &ssys);
2069      if (iuuconf != UUCONF_SUCCESS)
2070	{
2071	  ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
2072	  fret = FALSE;
2073	  continue;
2074	}
2075
2076      if (! fsquery_system (&ssys, &qlist, inow, zlocalname, csystems,
2077			    pazsystems, fnotsystems, iold, iyoung))
2078	fret = FALSE;
2079
2080      (void) uuconf_system_free (puuconf, &ssys);
2081      xfree ((pointer) *pz);
2082    }
2083
2084  /* Check for the local system in the list of execution files.  */
2085  if (qlist != NULL)
2086    {
2087      struct sxqtlist **pq;
2088
2089      for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext)
2090	{
2091	  if (strcmp ((*pq)->zsystem, zlocalname) == 0)
2092	    {
2093	      struct uuconf_system ssys;
2094	      struct sxqtlist *qfree;
2095
2096	      iuuconf = uuconf_system_info (puuconf, zlocalname, &ssys);
2097	      if (iuuconf != UUCONF_SUCCESS)
2098		{
2099		  if (iuuconf != UUCONF_NOT_FOUND)
2100		    {
2101		      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
2102		      fret = FALSE;
2103		      break;
2104		    }
2105
2106		  iuuconf = uuconf_system_local (puuconf, &ssys);
2107		  if (iuuconf != UUCONF_SUCCESS)
2108		    {
2109		      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
2110		      fret = FALSE;
2111		      break;
2112		    }
2113		  ssys.uuconf_zname = (char *) zlocalname;
2114		}
2115
2116	      if (! fsquery_show (&ssys, 0, 0L, *pq, inow, zlocalname,
2117				  csystems, pazsystems, fnotsystems,
2118				  iold, iyoung))
2119		fret = FALSE;
2120	      (void) uuconf_system_free (puuconf, &ssys);
2121	      qfree = *pq;
2122	      *pq = qfree->qnext;
2123	      ubuffree (qfree->zsystem);
2124	      xfree ((pointer) qfree);
2125	      break;
2126	    }
2127	}
2128    }
2129
2130  /* Print out information for any unknown systems for which we have
2131     execution files.  */
2132  while (qlist != NULL)
2133    {
2134      struct uuconf_system ssys;
2135      struct sxqtlist *qnext;
2136
2137      if (! funknown_system (puuconf, qlist->zsystem, &ssys))
2138	{
2139	  ulog (LOG_ERROR, "Executions queued up for unknown systems");
2140	  fret = FALSE;
2141	  break;
2142	}
2143
2144      if (! fsquery_show (&ssys, 0, 0L, qlist, inow, zlocalname,
2145			  csystems, pazsystems, fnotsystems, iold, iyoung))
2146	fret = FALSE;
2147      (void) uuconf_system_free (puuconf, &ssys);
2148      qnext = qlist->qnext;
2149      ubuffree (qlist->zsystem);
2150      xfree ((pointer) qlist);
2151      qlist = qnext;
2152    }
2153
2154  return fret;
2155}
2156
2157/* Query a single known system.  */
2158
2159static boolean
2160fsquery_system (qsys, pq, inow, zlocalname, csystems, pazsystems,
2161		fnotsystems, iold, iyoung)
2162     const struct uuconf_system *qsys;
2163     struct sxqtlist **pq;
2164     long inow;
2165     const char *zlocalname;
2166     int csystems;
2167     char **pazsystems;
2168     boolean fnotsystems;
2169     long iold;
2170     long iyoung;
2171{
2172  int cwork;
2173  long ifirstwork;
2174  char *zid;
2175  boolean fret;
2176
2177  if (! fsysdep_get_work_init (qsys, UUCONF_GRADE_LOW, 0))
2178    return FALSE;
2179
2180  cwork = 0;
2181  ifirstwork = 0L;
2182  zid = NULL;
2183  while (TRUE)
2184    {
2185      struct scmd s;
2186      long itime;
2187      char *zthisid;
2188
2189      if (! fsysdep_get_work (qsys, UUCONF_GRADE_LOW, 0, &s))
2190	return FALSE;
2191      if (s.bcmd == 'H')
2192	break;
2193
2194      zthisid = zsysdep_jobid (qsys, s.pseq);
2195      if (zid != NULL && strcmp (zid, zthisid) == 0)
2196	ubuffree (zthisid);
2197      else
2198	{
2199	  ++cwork;
2200	  ubuffree (zid);
2201	  zid = zthisid;
2202	}
2203
2204      itime = ixsysdep_work_time (qsys, s.pseq);
2205      if (ifirstwork == 0L || ifirstwork > itime)
2206	ifirstwork = itime;
2207    }
2208
2209  usysdep_get_work_free (qsys);
2210  ubuffree (zid);
2211
2212  /* Find the execution information, if any.  */
2213  while (*pq != NULL)
2214    {
2215      if (strcmp ((*pq)->zsystem, qsys->uuconf_zname) == 0)
2216	break;
2217      pq = &(*pq)->qnext;
2218    }
2219
2220  /* If there are no commands and no executions, don't print any
2221     information for this system.  */
2222  if (cwork == 0 && *pq == NULL)
2223    return TRUE;
2224
2225  fret = fsquery_show (qsys, cwork, ifirstwork, *pq, inow,
2226		       zlocalname, csystems, pazsystems, fnotsystems,
2227		       iold, iyoung);
2228
2229  if (*pq != NULL)
2230    {
2231      struct sxqtlist *qfree;
2232
2233      qfree = *pq;
2234      *pq = qfree->qnext;
2235      ubuffree (qfree->zsystem);
2236      xfree ((pointer) qfree);
2237    }
2238
2239  return fret;
2240}
2241
2242/* Print out the query information for a single system.  We handle the
2243   local system specially.  */
2244
2245static boolean
2246fsquery_show (qsys, cwork, ifirstwork, qxqt, inow, zlocalname,
2247	      csystems, pazsystems, fnotsystems, iold, iyoung)
2248     const struct uuconf_system *qsys;
2249     int cwork;
2250     long ifirstwork;
2251     struct sxqtlist *qxqt;
2252     long inow;
2253     const char *zlocalname;
2254     int csystems;
2255     char **pazsystems;
2256     boolean fnotsystems;
2257     long iold;
2258     long iyoung;
2259{
2260  boolean flocal;
2261  struct sstatus sstat;
2262  boolean fnostatus;
2263  struct tm stime;
2264  int cpad;
2265
2266  /* Make sure this is one of the systems we are printing.  */
2267  if (csystems > 0)
2268    {
2269      boolean fmatch;
2270      int i;
2271
2272      fmatch = fnotsystems;
2273      for (i = 0; i < csystems; i++)
2274	{
2275	  if (strcmp (pazsystems[i], qsys->uuconf_zname) == 0)
2276	    {
2277	      fmatch = ! fmatch;
2278	      break;
2279	    }
2280	}
2281      if (! fmatch)
2282	return TRUE;
2283    }
2284
2285  /* Make sure the commands are within the time bounds.  */
2286  if ((iold != (long) -1
2287       && (cwork == 0 || ifirstwork > iold)
2288       && (qxqt == NULL || qxqt->ifirst > iold))
2289      || (iyoung != (long) -1
2290	  && (cwork == 0 || ifirstwork < iyoung)
2291	  && (qxqt == NULL || qxqt->ifirst < iyoung)))
2292    return TRUE;
2293
2294  flocal = strcmp (qsys->uuconf_zname, zlocalname) == 0;
2295
2296  if (! flocal)
2297    {
2298      if (! fsysdep_get_status (qsys, &sstat, &fnostatus))
2299	return FALSE;
2300    }
2301
2302  printf ("%-10s %3dC (", qsys->uuconf_zname, cwork);
2303
2304  if (cwork == 0)
2305    {
2306      printf ("0 secs");
2307      cpad = 3;
2308    }
2309  else
2310    cpad = csunits_show (inow - ifirstwork);
2311
2312  printf (") ");
2313  while (cpad-- != 0)
2314    printf (" ");
2315
2316  if (qxqt == NULL)
2317    printf ("  0X (0 secs)   ");
2318  else
2319    {
2320      printf ("%3dX (", qxqt->cxqts);
2321      cpad = csunits_show (inow - qxqt->ifirst);
2322      printf (")");
2323      while (cpad-- != 0)
2324	printf (" ");
2325    }
2326
2327  if (flocal || fnostatus)
2328    {
2329      printf ("\n");
2330      if (! flocal)
2331	ubuffree (sstat.zstring);
2332      return TRUE;
2333    }
2334
2335  usysdep_localtime (sstat.ilast, &stime);
2336
2337  printf (" %02d-%02d %02d:%02d ",
2338	  stime.tm_mon + 1,stime.tm_mday, stime.tm_hour, stime.tm_min);
2339
2340  if (sstat.zstring == NULL)
2341    printf ("%s\n", azStatus[(int) sstat.ttype]);
2342  else
2343    {
2344      printf ("%s\n", sstat.zstring);
2345      ubuffree (sstat.zstring);
2346    }
2347
2348  return TRUE;
2349}
2350
2351/* Print a time difference in the largest applicable units.  */
2352
2353static int
2354csunits_show (idiff)
2355     long idiff;
2356{
2357  const char *zunit;
2358  long iunits;
2359  int cpad;
2360
2361  if (idiff > (long) 24 * (long) 60 * (long) 60)
2362    {
2363      iunits = idiff / ((long) 24 * (long) 60 * (long) 60);
2364      zunit = "day";
2365      cpad = 4;
2366    }
2367  else if (idiff > (long) 60 * 60)
2368    {
2369      iunits = idiff / (long) (60 * 60);
2370      zunit = "hour";
2371      cpad = 3;
2372    }
2373  else if (idiff > (long) 60)
2374    {
2375      iunits = idiff / (long) 60;
2376      zunit = "min";
2377      cpad = 4;
2378    }
2379  else
2380    {
2381      iunits = idiff;
2382      zunit = "sec";
2383      cpad = 4;
2384    }
2385
2386  printf ("%ld %s%s", iunits, zunit, iunits == 1 ? "" : "s");
2387
2388  if (iunits != 1)
2389    --cpad;
2390  if (iunits > 99)
2391    --cpad;
2392  if (iunits > 9)
2393    --cpad;
2394  return cpad;
2395}
2396
2397/* Give a list of all status entries for all machines that we have
2398   status entries for.  We need to get a list of status entries in a
2399   system dependent fashion, since we may have status for unknown
2400   systems.  */
2401
2402static boolean
2403fsmachines ()
2404{
2405  pointer phold;
2406  char *zsystem;
2407  boolean ferr;
2408  struct sstatus sstat;
2409
2410  if (! fsysdep_all_status_init (&phold))
2411    return FALSE;
2412
2413  while ((zsystem = zsysdep_all_status (phold, &ferr, &sstat)) != NULL)
2414    {
2415      struct tm stime;
2416
2417      usysdep_localtime (sstat.ilast, &stime);
2418      printf ("%-14s %02d-%02d %02d:%02d ", zsystem,
2419	      stime.tm_mon + 1, stime.tm_mday, stime.tm_hour,
2420	      stime.tm_min);
2421      if (sstat.zstring == NULL)
2422	printf ("%s", azStatus[(int) sstat.ttype]);
2423      else
2424	{
2425	  printf ("%s", sstat.zstring);
2426	  ubuffree (sstat.zstring);
2427	}
2428      ubuffree (zsystem);
2429      if (sstat.ttype != STATUS_TALKING
2430	  && sstat.cwait > 0)
2431	{
2432	  printf (" (%d %s", sstat.cretries,
2433		  sstat.cretries == 1 ? "try" : "tries");
2434	  if (sstat.ilast + sstat.cwait > ixsysdep_time ((long *) NULL))
2435	    {
2436	      usysdep_localtime (sstat.ilast + sstat.cwait, &stime);
2437	      printf (", next after %02d-%02d %02d:%02d",
2438		      stime.tm_mon + 1, stime.tm_mday, stime.tm_hour,
2439		      stime.tm_min);
2440	    }
2441	  printf (")");
2442	}
2443      printf ("\n");
2444    }
2445
2446  usysdep_all_status_free (phold);
2447
2448  return ! ferr;
2449}
2450