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