1 #include "scheduled.h"
2 #include "uogetopt.h"
3 #include "byte.h"
4 #include "bailout.h"
5 #include "buffer.h"
6 #include "env.h"
7 #include "str.h"
8 #include "attributes.h"
9 #include "alloc.h"
10 #include "error.h"
11 #include "api_dir.h"
12 #include "mssort.h"
13 #include "wrap_stat.h"
14 #include "fmt_tai.h"
15 #include "scan.h"
16 #include "open.h"
17 #include "getln.h"
18 #include <unistd.h>
19 
20 static const char *o_dir;
21 static int o_commands;
22 static int o_show_command;
23 static int o_short;
24 static int o_dot_as_home;
25 static int exitcode=0;
26 static const char *o_now=0;
27 static uogetopt2 myopts[]={
28 {'.',"dot-as-home",uogo_flag,UOGO_NOARG,&o_dot_as_home, 1 ,
29 "Use current directory instead of $HOME.",0,0},
30 {'c',"commands",uogo_flag,UOGO_NOARG,&o_commands, 1 ,
31 "List command files, not scheduled jobs.",
32 "The default is to list schedules. This option lists all prepared commands.",
33 0},
34 {'d',"dir",uogo_string,0,&o_dir, 0 ,
35 "Jobdirectory.",
36 "Jobs will be read from this directory. Note that this does not read from "
37 "DIR/.uschedule, but from DIR.","DIR"},
38 {  0,"now",uogo_string,0,&o_now, 0 ,
39 "Use FAKE-TIME instead of `now'.",
40 "FAKE-TIME must be given as seconds since epoch. This option is for meant "
41 "for the self check of the uschedule package only.","FAKE-TIME"},
42 {'s',"short",uogo_flag,UOGO_NOARG,&o_short, 1 ,
43 "Create a short listing.",0,0},
44 {'S',"show-command",uogo_flag,UOGO_NOARG,&o_show_command, 1 ,
45 "Show the whole command.",
46 "Lists the full command in addition to the listing output. This may be "
47 "useful to distinguish between multiple similary named commands.",0},
48 {0,"examples",uogo_print_help,UOGO_NOARG|UOGO_HIDDEN,0,0,"",
49 "The following three command lines all show the same result:\n"
50 "  cd /tmp ; uschedulelist\n"
51 "  cd ~ ; uschedulelist -.\n"
52 "  cd /tmp ; uschedulelist -d ~/.uschedule\n"
53 "but this one does not:\n"
54 "  cd /tmp ; uschedulelist -d ~\n"
55 "To list the registered commands use the --commands option:\n"
56 "  uschedulelist -c\n"
57 "",0},
58 {0,"author",uogo_print_help,UOGO_NOARG|UOGO_HIDDEN,0,0,"",
59 "Uwe Ohse, <uwe@ohse.de>",0},
60 {0,"copyright",uogo_print_help,UOGO_NOARG|UOGO_HIDDEN,0,0,"",
61 "This software is published under the TODO...",0},
62 {0,0,0,0,0,0,0,0,0}
63 };
64 static stralloc jobs;
65 static struct taia now;
66 static void die_write(void) attribute_noreturn;
die_write(void)67 static void die_write(void) {xbailout(111,errno,"failed to write",0,0,0);}
68 
69 static int
cmp_ji(const void * a,const void * b)70 cmp_ji(const void *a, const void *b)
71 {
72 	struct jobinfo *ja;
73 	struct jobinfo *jb;
74 	union {const struct jobinfo *c; struct jobinfo *u;} dq;
75 	unsigned int l;
76 	int i;
77 	struct taia ta,tb;
78 	dq.c=a; ja=dq.u;
79 	dq.c=b; jb=dq.u;
80 	/* the next two lines are for the sake of attribute_check_result */
81 	if (!find_next(ja,&now,&ta)) return -1;
82 	if (!find_next(jb,&now,&tb)) return 1;
83 	if (taia_less(&ta,&tb)) return -1;
84 	if (taia_less(&tb,&ta)) return  1;
85 	l=jb->idlen;
86 	if (ja->idlen<l)
87 		l=ja->idlen;
88 	i=byte_diff(ja->id,l,jb->id);
89 	if (i<0) return -1;
90 	if (i>0) return  1;
91 	if (ja->idlen>jb->idlen)
92 		return -1;
93 	return 1;
94 }
95 static int
cmp_commands(const void * a,const void * b)96 cmp_commands(const void *a, const void *b)
97 {
98 	int i;
99 	i=str_diff(a,b);
100 	if (i<0) return -1;
101 	if (i>0) return  1;
102 	if (str_len(a) > str_len(b))
103 		return -1;
104 	return 1;
105 }
106 static void
doonestr(unsigned int * l,const char * s)107 doonestr(unsigned int *l, const char *s)
108 {
109 	unsigned int sl=str_len(s);
110 	if (*l+sl>79) {
111 		if (-1==buffer_put(buffer_1,"\n  ",2)) die_write();
112 		*l=2;
113 	}
114 	if (-1==buffer_put(buffer_1,s,sl)) die_write();
115 }
116 
117 static void
show_command(const char * fname)118 show_command(const char *fname)
119 {
120   stralloc saline=STRALLOC_INIT;
121   int fd;
122   char bi[4096];
123   buffer i;
124 
125   fd=open_read(fname);
126   if (-1==fd) { warning(errno,"failed to open_read ",fname,0,0); return;}
127   buffer_init(&i,(buffer_op)read,fd,bi,sizeof(bi));
128   while (1)  {
129     int gotlf;
130     if (-1==getln(&i,&saline,&gotlf,'\n'))
131       xbailout(111,errno,"failed to read ",fname,0,0);
132     if (!saline.len) break;
133     if (-1==buffer_put(buffer_1,"    ",4)) die_write();
134     if (-1==buffer_put(buffer_1,saline.s,saline.len)) die_write();
135   }
136   close(fd);
137   stralloc_free(&saline);
138   if (-1==buffer_flush(buffer_1)) die_write();
139 }
140 static void
print_short_job(unsigned int maxlen,struct jobinfo * j)141 print_short_job(unsigned int maxlen, struct jobinfo *j)
142 {
143   static stralloc sa;
144   struct taia next;
145   uint64 ui64;
146   unsigned int l;
147   if (-1==buffer_put(buffer_1,j->id,j->idlen)) die_write();
148   l=j->idlen;
149   while (l<maxlen) {
150     if (-1==buffer_puts(buffer_1," ")) die_write();
151     l++;
152   }
153   if (-1==buffer_puts(buffer_1," (")) die_write();
154   l+=2;
155 
156   doonestr(&l,"next: ");
157   if (!find_next(j,&now,&next)) {
158     doonestr(&l,"never");
159   } else {
160     char buf[128];
161     if (!fmt_tai(buf,sizeof(buf),&next.sec))
162       doonestr(&l,buf);
163     else
164       doonestr(&l,"???");
165   }
166 
167   doonestr(&l,", last: ");
168 
169   ui64=HACK_TAIA_SEC(&j->lastrun);
170   if (0==ui64) {
171     doonestr(&l,"never");
172   } else {
173     char buf[128];
174     if (!fmt_tai(buf,sizeof(buf),&j->lastrun.sec))
175       doonestr(&l,buf);
176     else
177       doonestr(&l,"???");
178   }
179 
180   if (j->repeats) {
181     sa.len=0;
182     doonestr(&l,", runs left: ");
183     if (!stralloc_catuint0(&sa,j->repeats,0)) oom();
184     if (!stralloc_0(&sa)) oom();
185     doonestr(&l,sa.s);
186   }
187   if (j->every) {
188     sa.len=0;
189     doonestr(&l,", repeat every ");
190     if (!stralloc_catuint0(&sa,j->every,0)) oom();
191     if (!stralloc_0(&sa)) oom();
192     doonestr(&l," seconds");
193     doonestr(&l,sa.s);
194   }
195   doonestr(&l,")\n");
196   if (-1==buffer_flush(buffer_1)) die_write();
197 }
198 
199 static void
list_schedules(char ** filenames,int * flag)200 list_schedules(char **filenames, int *flag)
201 {
202   unsigned int i;
203   unsigned int count=0;
204   unsigned int maxlen=0;
205   struct jobinfo *j;
206   load_jobs(".",&jobs);
207 
208   for (i=0;jobs.s[i];i+=str_len(jobs.s+i)+1)
209     count++;
210   j=(struct jobinfo *)alloc(count*sizeof(*j));
211   if (!j)
212     oom();
213   count=0;
214   for (i=0;jobs.s[i];i+=str_len(jobs.s+i)+1) {
215     parse_job(jobs.s+i,&j[count]); /* load_jobs ensures this is OK */
216     if (j[count].idlen>maxlen)
217       maxlen=j[count].idlen;
218     count++;
219   }
220   mssort((char *)j,count,sizeof(*j),cmp_ji);
221   for (i=0;i<count;i++) {
222     if (filenames[0]) {
223       unsigned int k;
224       for (k=0;filenames[k];k++) {
225 	unsigned int l=str_len(filenames[k]);
226 	if (l!=j[i].idlen)
227 	  continue;
228 	if (!byte_equal(j[i].id,l,filenames[k]))
229 	  continue;
230 	break;
231       }
232       if (!filenames[k])
233 	continue;
234       flag[k]=1;
235     }
236     if (o_short)
237       print_short_job(maxlen,&j[i]);
238     else
239       print_job(&j[i],&now);
240     if (o_show_command) {
241       stralloc sa;
242       make_name(&sa,&j[i]);
243       show_command(sa.s);
244       stralloc_free(&sa);
245     }
246   }
247 }
248 static void
print_command(const char * s)249 print_command(const char *s)
250 {
251   struct wrap_stat st;
252   static stralloc sa;
253   char buf[128];
254 
255   if (-1==buffer_puts(buffer_1,s)) die_write();
256   if (-1==buffer_puts(buffer_1,"\n")) die_write();
257 
258   if (-1==wrap_stat(s,&st)) {
259     int e=errno;
260     warning(e,"failed to stat ",s,0,0);
261     exitcode=1;
262     if (-1==buffer_puts(buffer_1,"  failed to stat command file: "))
263 	    die_write();
264     if (-1==buffer_puts(buffer_1,error_str(e))) die_write();
265     if (-1==buffer_puts(buffer_1,"\n")) die_write();
266     if (-1==buffer_flush(buffer_1)) die_write();
267     return;
268   }
269   if (!stralloc_copys(&sa,"  size: ")) oom();
270   if (!stralloc_catuint0(&sa,st.size,0)) oom();
271   if (!stralloc_cats(&sa,"\n  links: ")) oom();
272   if (!stralloc_catuint0(&sa,st.nlink,0)) oom();
273   if (!stralloc_cats(&sa,"\n  modification: ")) oom();
274 
275   if (!fmt_tai(buf,sizeof(buf),&st.mtime.sec))
276   if (!stralloc_cats(&sa,buf)) oom();
277   if (!stralloc_cats(&sa,"\n")) oom();
278 
279   if (-1==buffer_put(buffer_1,sa.s,sa.len)) die_write();
280   if (-1==buffer_flush(buffer_1)) die_write();
281 }
282 
283 static void
print_short_command(unsigned int maxlen,const char * s)284 print_short_command(unsigned int maxlen, const char *s)
285 {
286   struct wrap_stat st;
287   static stralloc sa;
288   char buf[128];
289   unsigned int l;
290 
291   if (-1==buffer_puts(buffer_1,s)) die_write();
292   l=str_len(s);
293   while (l<maxlen) {
294     if (-1==buffer_puts(buffer_1," ")) die_write();
295     l++;
296   }
297   if (-1==buffer_puts(buffer_1," (")) die_write();
298   l+=2;
299 
300   if (-1==wrap_stat(s,&st)) {
301     int e=errno;
302     warning(e,"failed to stat ",s,0,0);
303     exitcode=1;
304     if (-1==buffer_puts(buffer_1,error_str(e))) die_write();
305     if (-1==buffer_puts(buffer_1,")\n")) die_write();
306     if (-1==buffer_flush(buffer_1)) die_write();
307     return;
308   }
309   sa.len=0;
310   if (!stralloc_catuint0(&sa,st.size,0)) oom();
311   if (!stralloc_0(&sa)) oom();
312   doonestr(&l,sa.s);
313   doonestr(&l," B, ");
314 
315   sa.len=0;
316   if (!stralloc_catuint0(&sa,st.nlink,0)) oom();
317   if (!stralloc_0(&sa)) oom();
318   doonestr(&l,sa.s);
319   doonestr(&l," L, ");
320   if (!fmt_tai(buf,sizeof(buf),&st.mtime.sec))
321   doonestr(&l,buf);
322   doonestr(&l,")\n");
323   if (-1==buffer_flush(buffer_1)) die_write();
324 }
325 
326 static void
list_commands(char ** filenames,int * flag)327 list_commands(char **filenames, int *flag)
328 {
329   unsigned int j;
330   int count=0;
331   unsigned int maxlen=0;
332   static stralloc sa;
333   const char *s;
334   unsigned int dirflag;
335 
336   if (-1==chdir(IDDIR))
337     xbailout(111,errno,"failed to chdir to ", IDDIR,  0,0);
338   count=api_dir_read(&sa,".");
339   if (-1==count)
340     xbailout(111,errno,"failed to read .",0,0,0);
341   if (!count)
342     return;
343 
344   if (-1==api_dir_sort(&sa,cmp_commands)) oom();
345 
346   for (s=api_dir_walkstart(&sa,&dirflag);s;s=api_dir_walknext(&sa,&dirflag)) {
347     if (filenames[0]) {
348       for (j=0;filenames[j];j++) {
349 	if (str_equal(s,filenames[j]))
350 	  break;
351       }
352       if (!filenames[j])
353 	continue;
354       flag[j]=1;
355     }
356     if (o_short)
357       print_short_command(maxlen,s);
358     else
359       print_command(s);
360     if (o_show_command)
361       show_command(s);
362   }
363 }
364 uogetopt_env myoptenv={
365 "uschedulelist",PACKAGE,VERSION,
366 "uschedulelist [JOB-ID ...]",
367 "This program list the scheduled jobs.",
368 "This program by default lists the contents of the ~/.uschedule directory. "
369 "If any JOB-ID is given then only these jobs are listed.\n"
370 "The directory the jobs are read from can be changed by use of the "
371 "--dir (-d) or -. options.\n",
372 "Report bugs to uschedule@lists.ohse.de",
373 0,0,0,0,uogetopt_out,myopts
374 };
375 
376 int
main(int argc,char ** argv)377 main(int argc, char **argv)
378 {
379 	int *flag;
380 	taia_now(&now);
381 	bailout_progname(argv[0]);
382 	flag_bailout_fatal_begin=3;
383 	uogo_posixmode=1;
384 	myoptenv.program=flag_bailout_log_name;
385 	uogetopt_parse(&myoptenv,&argc,argv);
386 	if (o_now) {
387 	  unsigned long ul;
388 	  int l;
389 	  l=scan_ulong(o_now,&ul);
390 	  if (!l|o_now[l])
391 	    xbailout(2,0,"cannot understand `now' value `",o_now,"'",0);
392 	  taia_uint(&now,0);
393 	  tai_unix(&now.sec,ul);
394 	}
395 	change_dir(0,o_dir,o_dot_as_home);
396 	if (argc==1)
397 		flag=0;
398 	else {
399 		int i;
400 		flag=(int *)alloc((argc-1)*sizeof(int));
401 		if (!flag) oom();
402 		for (i=0;i<argc-1;i++)
403 			flag[i]=0;
404 	}
405 	if (o_commands)
406 		list_commands(argv+1,flag);
407 	else
408 		list_schedules(argv+1,flag);
409 	if (flag) {
410 		int i;
411 		for (i=0;i<argc-1;i++)
412 			if (!flag[i]) {
413 				warning(0,argv[i+1],": not found",0,0);
414 				exitcode=1;
415 			}
416 	}
417 	return exitcode;
418 }
419