ezmlmx 0.69
ezmlmx
Loading...
Searching...
No Matches
ezmlm-cron.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <pwd.h>
4#include <unistd.h>
5#include "stralloc.h"
6#include "getoptb.h"
7#include "buffer.h"
8#include "error.h"
9#include "str.h"
10#include "fmt.h"
11#include "wait.h"
12#include "readwrite.h"
13#include "sig.h"
14#include "case.h"
15#include "scan.h"
16#include "open.h"
17#include "lock.h"
18#include "byte.h"
19#include "getln.h"
20#include "auto_qmail.h"
21#include "auto_cron.h"
22#include "auto_version.h"
23#include "errtxt.h"
24#include "idx.h"
25#include "wrap.h"
26#include "logmsg.h"
27#include "lockfile.h"
28
29#define WHO "ezmlm-cron"
30
31int rename(const char *,const char *); // stdio.h
32
33static void die_usage() { logmsg(WHO,100,USAGE,"ezmlm-cron [-cCdDlLvV] [-w dow] [-t hh:mm] [-i hrs] listadr code"); }
34static void die_dow() { logmsg(WHO,100,FATAL,ERR_DOW); }
35static void die_nomem() { logmsg(WHO,111,FATAL,ERR_NOMEM); }
36
37unsigned long deltah = 24L; /* default interval 24h */
38unsigned long hh = 4L; /* default time 04:12 */
39unsigned long mm = 12L;
40const char *dow = "*"; /* day of week */
41const char *qmail_inject = "/bin/qmail-inject ";
42char strnum[FMT_ULONG];
43unsigned long uid,euid;
44
45stralloc line = {0};
46stralloc rp = {0};
47stralloc addr = {0};
48stralloc user = {0};
49stralloc euser = {0};
50stralloc dir = {0};
51stralloc listaddr = {0};
52
53struct passwd *ppasswd;
54
58unsigned long dh,t;
59int founduser = 0;
60int listmatch = 0;
61int flagconfig = 0;
62int flagdelete = 0;
63int flaglist = 0;
64int flagdigit = 0;
67int foundmatch = 0;
68int nolists = 0;
69unsigned long maxlists;
70unsigned int pos, pos2, poslocal, len;
71unsigned int lenhost, lenlocal;
72unsigned int part0start, part0len;
74
75char *local = (char *) 0; /* list = local@host */
76const char *host = (char *) 0;
77const char *code = (char *) 0; /* digest code */
78const char *cp;
79
81{
82 if (!stralloc_0(&line)) die_nomem();
83 logmsg(WHO,100,FATAL,B(TXT_EZCRONRC ERR_SYNTAX,line.s));
84}
85
86void die_argument() { logmsg(WHO,100,FATAL,ERR_NOT_CLEAN); }
87
94
95int isclean(char *addr,int flagaddr)
96{
97 unsigned int pos;
98 char ch;
99 char *cp;
100
101 if (flagaddr) { /* shoud have one '@' */
102 pos = str_chr(addr,'@');
103 if (!pos || !addr[pos])
104 return 0; /* at least 1 char for local */
105 if (!addr[pos+1])
106 return 0; /* host must be at least 1 char */
107 pos++;
108 case_lowerb(addr+pos,str_len(addr)-pos);
109 } else
110 pos = 0;
111
112 pos += str_chr(addr + pos,'@');
113 if (addr[pos]) /* but no more */
114 return 0;
115 cp = addr;
116
117 while ((ch = *(cp++)))
118 if (!(ch >= 'a' && ch <= 'z') &&
119 !(ch >= 'A' && ch <= 'Z') &&
120 !(ch >= '0' && ch <= '9') &&
121 ch != '.' && ch != '-' && ch != '_' && ch != '@')
122 return 0;
123 return 1;
124}
125
126char inbuf[512];
127buffer bi;
128char outbuf[512];
129buffer bo;
130
131int main(int argc,char **argv)
132{
133 int child;
134 const char *sendargs[4];
135
136 umask(077);
137 sig_pipeignore();
138
139 while ((opt = getoptb(argc,argv,"cCdDi:lLt:w:vV")) != opteof)
140 switch (opt) {
141 case 'c': flagconfig = 1; break;
142 case 'C': flagconfig = 0; break;
143 case 'd': flagdelete = 1; break;
144 case 'D': flagdelete = 0; break;
145 case 'i': scan_ulong(optarg,&deltah); break;
146 case 'l': flaglist = 1; break;
147 case 'L': flaglist = 0; break;
148 case 't': pos = scan_ulong(optarg,&hh);
149 if (!optarg[pos++] == ':') die_usage();
150 pos = scan_ulong(optarg + pos,&mm);
151 break;
152 case 'w': dow = optarg;
153 cp = optarg - 1;
154 while (*(++cp)) {
155 if (*cp >= '0' && *cp <= '7') {
156 if (flagdigit) die_dow();
157 flagdigit = 1;
158 } else if (*cp == ',') {
159 if (!flagdigit) die_dow();
160 flagdigit = 0;
161 } else
162 die_dow();
163 }
164 break;
165 case 'v':
166 case 'V': logmsg(WHO,0,VERSION,auto_version);
167 default: die_usage();
168 }
169 if (flaglist + flagdelete + flagconfig > 1)
170 logmsg(WHO,100,FATAL,ERR_EXCLUSIVE);
171 uid = getuid();
172 if (uid && !(euid = geteuid()))
173 logmsg(WHO,100,FATAL,ERR_SUID);
174 if (!(ppasswd = getpwuid(uid)))
175 logmsg(WHO,100,FATAL,ERR_UID);
176 if (!stralloc_copys(&user,ppasswd->pw_name)) die_nomem();
177 if (!stralloc_0(&user)) die_nomem();
178 if (!(ppasswd = getpwuid(euid)))
179 logmsg(WHO,100,FATAL,ERR_EUID);
180 if (!stralloc_copys(&dir,ppasswd->pw_dir)) die_nomem();
181 if (!stralloc_0(&dir)) die_nomem();
182 if (!stralloc_copys(&euser,ppasswd->pw_name)) die_nomem();
183 if (!stralloc_0(&euser)) die_nomem();
184
185 if (chdir(dir.s) == -1)
186 logmsg(WHO,111,FATAL,B(ERR_SWITCH,dir.s));
187
188 local = argv[optind++]; /* list address, optional for -c & -l */
189 if (!local) {
190 if (!flagconfig && !flaglist) die_usage();
191 lenlocal = 0;
192 lenhost = 0;
193 } else {
194 if (!stralloc_copys(&listaddr,local)) die_nomem();
195 if (!isclean(local,1)) die_argument();
196 pos = str_chr(local,'@');
197 lenlocal = pos;
198 local[pos] = '\0';
199 host = local + pos + 1;
200 lenhost = str_len(host);
201 code = argv[optind];
202 if (!code) { /* ignored for -l, -c, and -d */
204 /* get away with not putting code for delete */
205 code = "a"; /* a hack - so what! */
206 else
207 die_usage();
208 } else
209 if (!isclean(code,0))
210 die_argument();
211 }
212 if ((fdin = open_read(TXT_EZCRONRC)) == -1)
213 logmsg(WHO,111,FATAL,B(ERR_OPEN,dir.s,"/","TXT_EZCRONRC"));
214 /* first line is special */
215 buffer_init(&bi,buffer_unixread,fdin,inbuf,sizeof(inbuf));
216 if (getln(&bi,&line,&match,'\n') == -1)
217 logmsg(WHO,111,FATAL,B(ERR_OPEN,dir.s,"/","TXT_EZCRONRC"));
218
219 if (!match)
220 logmsg(WHO,111,FATAL,B(ERR_OPEN,dir.s,"/","TXT_EZCRONRC"));
221 /* (since we have match line.len has to be >= 1) */
222 line.s[line.len - 1] = '\0';
223 if (!isclean(line.s,0)) /* host for bounces */
224 logmsg(WHO,100,INFO,B(ERR_CFHOST,dir.s,"/","TXT_EZCRONRC"));
225 if (!stralloc_copys(&rp,line.s)) die_nomem();
226
227 match = 1;
228 for(;;) {
229 if (!match) break; /* to allow last line without '\n' */
230 if (getln(&bi,&line,&match,'\n') == -1)
231 logmsg(WHO,111,FATAL,B(ERR_READ,dir.s,"/","TXT_EZCRONRC"));
232 if (!line.len)
233 break;
234 line.s[line.len-1] = '\0';
235 if (!case_startb(line.s,line.len,user.s))
236 continue;
237 pos = user.len - 1;
238 if (pos >= line.len || line.s[pos] != ':')
239 continue;
240 founduser = 1; /* got user line */
241 break;
242 }
243 close(fdin);
244 if (!founduser)
245 logmsg(WHO,100,FATAL,ERR_BADUSER);
246
247 if (flagconfig) {
248 line.s[line.len-1] = '\n'; /* not very elegant ;-) */
249 buffer_init(&bo,buffer_unixwrite,1,outbuf,sizeof(outbuf));
250 if (buffer_put(&bo,line.s,line.len) == -1)
251 logmsg(WHO,111,FATAL,B(ERR_WRITE,"stdout"));
252 if (buffer_flush(&bo) == -1)
253 logmsg(WHO,111,FATAL,B(ERR_WRITE,"stdout"));
254 _exit(0);
255 }
256 ++pos; /* points to first ':' */
257 len = str_chr(line.s+pos,':'); /* second ':' */
258 if (!line.s[pos + len])
259 die_syntax();
260 if (!local) { /* only -d and std left */
261 localmatch = 1;
262 hostmatch = 1;
263 } else {
264 hostmatch = 0;
265 if (len <= str_len(local))
266 if (!str_diffn(line.s+pos,local,len))
267 localmatch = 1;
268 }
269 pos += len + 1;
270 len = str_chr(line.s + pos,':'); /* third */
271 if (!line.s[pos + len])
272 die_syntax();
273 if (local) { /* check host */
274 if (len == 0) /* empty host => any host */
275 hostmatch = 1;
276 else
277 if (len == str_len(host))
278 if (!case_diffb(line.s+pos,len,host))
279 hostmatch = 1;
280 }
281 pos += len + 1;
282 pos += scan_ulong(line.s+pos,&maxlists);
283 if (line.s[pos]) { /* check additional lists */
284 if (line.s[pos] != ':')
285 die_syntax();
286 if (line.s[pos+1+str_chr(line.s+pos+1,':')])
287 die_syntax(); /* reminder lists are not separated by ':' */
288 /* otherwise a ':' or arg miscount will die silently */
289 if (local) {
290 while (++pos < line.len) {
291 len = str_chr(line.s + pos,'@');
292 if (len == lenlocal && !str_diffn(line.s + pos,local,len)) {
293 pos += len;
294 if (!line.s[pos]) break;
295 pos++;
296 len = str_chr(line.s+pos,',');
297 if (len == lenhost && !case_diffb(line.s+pos,len,host)) {
298 listmatch = 1;
299 break;
300 }
301 }
302 pos += len;
303 }
304 }
305 }
306 if (!listmatch) {
307 if (!hostmatch)
308 logmsg(WHO,100,FATAL,ERR_BADHOST);
309 if (!localmatch)
310 logmsg(WHO,100,FATAL,ERR_BADLOCAL);
311 }
312 /* assemble correct line */
313 if (!flaglist) {
314 if (!stralloc_copyb(&addr,strnum,fmt_ulong(strnum,mm))) die_nomem();
315 if (!stralloc_cats(&addr," ")) die_nomem();
316 dh = 0L;
317 if (deltah <= 3L) dh = deltah;
318 else if (deltah <= 6L) dh = 6L;
319 else if (deltah <= 12L) dh = 12L;
320 else if (deltah <= 24L) dh = 24L;
321 else if (deltah <= 48L) {
322 if (dow[0] == '*') dow = "1,3,5";
323 } else if (deltah <= 72L) {
324 if (dow[0] == '*') dow = "1,4";
325 } else
326 if (dow[0] == '*') dow = "1";
327
328 if (!dh) {
329 if (!stralloc_cats(&addr,"*")) die_nomem();
330 } else {
331 if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,hh))) die_nomem();
332 for (t = hh + dh; t < hh + 24L; t+=dh) {
333 if (!stralloc_cats(&addr,",")) die_nomem();
334 if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,t % 24L))) die_nomem();
335 }
336 }
337 if (!stralloc_cats(&addr," * * ")) die_nomem();
338 if (!stralloc_cats(&addr,dow)) die_nomem();
339 if (!stralloc_cats(&addr," ")) die_nomem();
340 part0start = addr.len; /* /var/qmail/bin/qmail-inject */
341 if (!stralloc_cats(&addr,auto_qmail)) die_nomem();
342 if (!stralloc_cats(&addr,qmail_inject)) die_nomem();
343 part0len = addr.len - part0start;
344 if (!stralloc_cats(&addr,local)) die_nomem();
345 if (!stralloc_cats(&addr,"-dig-")) die_nomem();
346 if (!stralloc_cats(&addr,code)) die_nomem();
347 if (!stralloc_cats(&addr,"@")) die_nomem();
348 if (!stralloc_cats(&addr,host)) die_nomem();
349 /* feed 'Return-Path: <user@host>' to qmail-inject */
350 if (!stralloc_cats(&addr,"%Return-path: <")) die_nomem();
351 if (!stralloc_cats(&addr,user.s)) die_nomem();
352 if (!stralloc_cats(&addr,"@")) die_nomem();
353 if (!stralloc_cat(&addr,&rp)) die_nomem();
354 if (!stralloc_cats(&addr,">\n")) die_nomem();
355 }
356 if (!stralloc_0(&addr)) die_nomem();
357
358 if (!flaglist) {
359 /* now to rewrite crontab we need to lock */
360 fdlock = lockfile("crontabl");
361 } /* if !flaglist */
362 if ((fdin = open_read("crontab")) == -1) {
363 if (errno != ENOENT)
364 logmsg(WHO,111,FATAL,B(ERR_READ,dir.s,"/crontab"));
365 } else
366 buffer_init(&bi,buffer_unixread,fdin,inbuf,sizeof(inbuf));
367 if (flaglist)
368 buffer_init(&bo,buffer_unixwrite,1,outbuf,sizeof(outbuf));
369 else {
370 if ((fdout = open_trunc("crontabn")) == -1)
371 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir.s,"/crontabn "));
372 buffer_init(&bo,buffer_unixwrite,fdout,outbuf,sizeof(outbuf));
373 }
374 line.len = 0;
375
376 if (fdin != -1) {
377 for (;;) {
378 if (!flaglist && line.len) {
379 line.s[line.len-1] = '\n';
380 if (buffer_put(&bo,line.s,line.len) == -1)
381 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir.s,"/crontabn"));
382 }
383 if (getln(&bi,&line,&match,'\n') == -1)
384 logmsg(WHO,111,FATAL,B(ERR_READ,dir.s,"/crontab"));
385 if (!match)
386 break;
387 flagours = 0; /* assume entry is not ours */
388 foundlocal = 0;
389 line.s[line.len - 1] = '\0'; /* match so at least 1 char */
390 pos = 0;
391 while (line.s[pos] == ' ' && line.s[pos] == '\t') ++pos;
392 if (line.s[pos] == '#')
393 continue; /* cron comment */
394 pos = str_chr(line.s,'/');
395 if (!str_start(line.s+pos,auto_qmail)) continue;
396 pos += str_len(auto_qmail);
397 if (!str_start(line.s+pos,qmail_inject)) continue;
398 pos += str_len(qmail_inject);
399 poslocal = pos;
400 pos = byte_rchr(line.s,line.len,'<'); /* should be Return-Path: < */
401 if (pos == line.len)
402 continue; /* not ezmlm-cron line */
403 pos++;
404 len = str_chr(line.s+pos,'@');
405 if (len == user.len - 1 && !str_diffn(line.s+pos,user.s,len)) {
406 flagours = 1;
407 ++nolists; /* belongs to this user */
408 }
409 if (!local) {
410 foundlocal = 1;
411 } else {
412 pos = poslocal + str_chr(line.s+poslocal,'@');
413 if (pos + lenhost +1 >= line.len) continue;
414 if (case_diffb(line.s+pos+1,lenhost,host)) continue;
415 if (line.s[pos+lenhost+1] != '%') continue;
416 /* check local */
417 if (poslocal + lenlocal + 5 >= line.len) continue;
418 if (!str_start(line.s+poslocal,local)) continue;
420 if (!str_start(line.s+pos2,"-dig-")) continue;
421 foundlocal = 1;
422 }
423 if (foundlocal) {
424 foundmatch = 1;
425 if (flaglist && (local || flagours)) {
426 if (buffer_put(&bo,line.s,line.len) == -1)
427 logmsg(WHO,111,FATAL,B(ERR_WRITE,"stdout"));
428 if (buffer_put(&bo,"\n",1) == -1)
429 logmsg(WHO,111,FATAL,B(ERR_WRITE,"stdout"));
430 }
431 line.len = 0; /* same - kill line */
432 if (flagours)
433 --nolists;
434 }
435 }
436 close(fdin);
437 }
438 if (flaglist) {
439 if (buffer_flush(&bo) == -1)
440 logmsg(WHO,111,FATAL,B(ERR_FLUSH,"stdout"));
441 if (foundmatch) /* means we had a match */
442 _exit(0);
443 else
444 logmsg(WHO,100,FATAL,ERR_NO_MATCH);
445 }
446 /* only -d and regular use left */
447
448 if (nolists >= maxlists && !flagdelete)
449 logmsg(WHO,100,FATAL,ERR_LISTNO);
450 if (!flagdelete)
451 if (buffer_put(&bo,addr.s,addr.len-1) == -1)
452 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir.s,"/crontabn"));
453 if (flagdelete && !foundlocal)
454 logmsg(WHO,100,FATAL,ERR_NO_MATCH);
455 if (buffer_flush(&bo) == -1)
456 logmsg(WHO,111,FATAL,B(ERR_FLUSH,dir.s,"/crontabn"));
457 if (fsync(fdout) == -1)
458 logmsg(WHO,111,FATAL,B(ERR_SYNC,dir.s,"/crontabn"));
459 if (close(fdout) == -1)
460 logmsg(WHO,111,FATAL,B(ERR_CLOSE,dir.s,"/crontabn"));
461 if (rename("crontabn","crontab") == -1)
462 logmsg(WHO,111,FATAL,B(ERR_MOVE,dir.s,"/crontabn"));
463 sendargs[0] = "sh";
464 sendargs[1] = "-c";
465
466 if (!stralloc_copys(&line,auto_cron)) die_nomem();
467 if (!stralloc_cats(&line,"/crontab '")) die_nomem();
468 if (!stralloc_cats(&line,dir.s)) die_nomem();
469 if (!stralloc_cats(&line,"/crontab'")) die_nomem();
470 if (!stralloc_0(&line)) die_nomem();
471 sendargs[2] = line.s;
472 sendargs[3] = 0;
473 if ((child = wrap_fork()) == 0) {
474 if (setreuid(euid,euid) == -1)
475 logmsg(WHO,100,FATAL,ERR_SETUID);
476 wrap_execvp(sendargs);
477 }
478 /* parent */
479 switch (wrap_waitpid(child)) {
480 case 0: _exit(0);
481 default: logmsg(WHO,111,FATAL,ERR_CRONTAB);
482 }
483
484 return 0;
485}
stralloc listaddr
Definition ezmlm-cron.c:51
unsigned long t
Definition ezmlm-cron.c:58
int opt
Definition ezmlm-cron.c:55
int nolists
Definition ezmlm-cron.c:68
void die_syntax()
Definition ezmlm-cron.c:80
unsigned int lenhost
Definition ezmlm-cron.c:71
const char * cp
Definition ezmlm-cron.c:78
int rename(const char *, const char *)
int fdin
Definition ezmlm-cron.c:73
unsigned int part0start
Definition ezmlm-cron.c:72
int flaglist
Definition ezmlm-cron.c:63
unsigned int poslocal
Definition ezmlm-cron.c:70
int founduser
Definition ezmlm-cron.c:59
stralloc user
Definition ezmlm-cron.c:48
int fdout
Definition ezmlm-cron.c:73
unsigned int lenlocal
Definition ezmlm-cron.c:71
unsigned int len
Definition ezmlm-cron.c:70
int foundmatch
Definition ezmlm-cron.c:67
int flagours
Definition ezmlm-cron.c:65
unsigned int part0len
Definition ezmlm-cron.c:72
int hostmatch
Definition ezmlm-cron.c:56
int flagdigit
Definition ezmlm-cron.c:64
unsigned long deltah
Definition ezmlm-cron.c:37
int localmatch
Definition ezmlm-cron.c:57
unsigned long hh
Definition ezmlm-cron.c:38
int listmatch
Definition ezmlm-cron.c:60
int foundlocal
Definition ezmlm-cron.c:66
int flagconfig
Definition ezmlm-cron.c:61
struct passwd * ppasswd
Definition ezmlm-cron.c:53
unsigned int pos2
Definition ezmlm-cron.c:70
const char * code
Definition ezmlm-cron.c:77
const char * dow
Definition ezmlm-cron.c:40
unsigned long maxlists
Definition ezmlm-cron.c:69
stralloc rp
Definition ezmlm-cron.c:46
stralloc addr
Definition ezmlm-cron.c:47
const char * qmail_inject
Definition ezmlm-cron.c:41
int isclean(char *addr, int flagaddr)
Definition ezmlm-cron.c:95
int fdlock
Definition ezmlm-cron.c:73
unsigned long dh
Definition ezmlm-cron.c:58
void die_argument()
Definition ezmlm-cron.c:86
int flagdelete
Definition ezmlm-cron.c:62
stralloc euser
Definition ezmlm-cron.c:49
unsigned long mm
Definition ezmlm-cron.c:39
#define WHO
Definition author.c:1
void die_nomem()
Definition getconf.c:17
int main()
Definition ezmlm-weed.c:69
char inbuf[1024]
char outbuf[1024]
char * dir
buffer bi
buffer bo
char * host
Definition ezmlm-cgi.c:107
char * local
Definition ezmlm-cgi.c:106
int child
Definition ezmlm-cgi.c:143
unsigned long uid
Definition ezmlm-cgi.c:134
int match
Definition ezmlm-cgi.c:140
unsigned long euid
Definition ezmlm-cgi.c:134
#define TXT_EZCRONRC
Definition idx.h:263
const char auto_version[]
int lockfile(const char *)
Definition lockfile.c:15
void wrap_execvp(const char **argv)
Definition wrap_execv.c:30
int wrap_fork(void)
Definition wrap_fork.c:15
int wrap_waitpid(int pid)
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_FLUSH
Definition errtxt.h:20
#define ERR_NOMEM
Definition errtxt.h:14
#define ERR_SYNC
Definition errtxt.h:22
#define ERR_OPEN
Definition errtxt.h:30
#define ERR_MOVE
Definition errtxt.h:29
#define ERR_EUID
Definition errtxt.h:133
#define ERR_CRONTAB
Definition errtxt.h:142
#define ERR_EXCLUSIVE
Definition errtxt.h:141
#define ERR_LISTNO
Definition errtxt.h:137
#define ERR_CFHOST
Definition errtxt.h:140
#define ERR_BADHOST
Definition errtxt.h:135
#define ERR_READ
Definition errtxt.h:18
#define ERR_SETUID
Definition errtxt.h:139
#define ERR_SYNTAX
Definition errtxt.h:121
#define ERR_DOW
Definition errtxt.h:129
#define ERR_UID
Definition errtxt.h:132
#define ERR_SWITCH
Definition errtxt.h:42
#define ERR_WRITE
Definition errtxt.h:17
#define ERR_CLOSE
Definition errtxt.h:16
#define ERR_NO_MATCH
Definition errtxt.h:138
#define ERR_SUID
Definition errtxt.h:131
#define ERR_BADUSER
Definition errtxt.h:134
#define ERR_BADLOCAL
Definition errtxt.h:136
#define ERR_NOT_CLEAN
Definition errtxt.h:130
const char auto_cron[]
Definition auto_cron.c:1
const char auto_qmail[]
Definition auto_qmail.c:1
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32