Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/proc.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2009 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* Mail -- a mail program
71
*
72
* Process control.
73
*/
74
75
#include "mailx.h"
76
77
#define READ 0
78
#define WRITE 1
79
80
static void
81
register_file(FILE* fp, int pid)
82
{
83
struct file* fpp;
84
85
if (!(fpp = (struct file*)malloc(sizeof *fpp)))
86
note(PANIC, "Out of space");
87
fpp->fp = fp;
88
fpp->pid = pid;
89
fpp->link = state.files;
90
state.files = fpp;
91
}
92
93
/*
94
* return std stream for file,mode
95
* 0 if no match
96
*/
97
98
FILE*
99
filestd(char* file, char* mode)
100
{
101
if (*mode == 'r') {
102
if (streq(file, "-") || streq(file, "/dev/stdin") || streq(file, "/dev/fd/0"))
103
return stdin;
104
}
105
else if (*mode == 'w') {
106
if (streq(file, "-") || streq(file, "/dev/stdout") || streq(file, "/dev/fd/1"))
107
return stdout;
108
else if (streq(file, "/dev/stderr") || streq(file, "/dev/fd/2"))
109
return stderr;
110
}
111
return 0;
112
}
113
114
/*
115
* fopen() with some extra mode prefixes:
116
*
117
* E enable error messages
118
* I don't register_file (fclose to close)
119
* M umask(~MAILMODE)
120
* R file must be S_ISREG()
121
* X expand() file name before open
122
*
123
* Open fd is set close-on-exec.
124
*/
125
126
FILE*
127
fileopen(char* file, char* mode)
128
{
129
FILE* fp;
130
int n;
131
int ignore = 0;
132
int mask = 0;
133
int regular = 0;
134
int verbose = 0;
135
136
for (;; mode++) {
137
switch (*mode) {
138
case 'E':
139
verbose = 1;
140
continue;
141
case 'I':
142
ignore = 1;
143
continue;
144
case 'M':
145
mask = 1;
146
continue;
147
case 'R':
148
regular = 1;
149
continue;
150
case 'X':
151
if (!(file = expand(file, 1)))
152
return 0;
153
continue;
154
}
155
break;
156
}
157
if (fp = filestd(file, mode))
158
return fp;
159
if (mask)
160
n = umask(~MAILMODE);
161
fp = fopen(file, mode);
162
if (mask) {
163
umask(n);
164
if (state.readonly && streq(mode, "w"))
165
chmod(file, S_IRUSR);
166
}
167
if (fp) {
168
if (fstat(fileno(fp), &state.openstat)) {
169
fclose(fp);
170
if (verbose)
171
note(SYSTEM, "%s", file);
172
return 0;
173
}
174
if (S_ISDIR(state.openstat.st_mode)) {
175
fclose(fp);
176
errno = EISDIR;
177
if (verbose)
178
note(SYSTEM, "%s", file);
179
return 0;
180
}
181
if (regular && !S_ISREG(state.openstat.st_mode)) {
182
fclose(fp);
183
errno = EFTYPE;
184
if (verbose)
185
note(SYSTEM, "%s", file);
186
return 0;
187
}
188
if (!ignore)
189
register_file(fp, 0);
190
fcntl(fileno(fp), F_SETFD, 1);
191
#if 0 && MORE_DISCIPLINE
192
sfsetbuf(fp, (void*)fp, SF_UNBOUND);
193
#endif
194
}
195
else if (verbose)
196
note(SYSTEM, "%s", file);
197
return fp;
198
}
199
200
/*
201
* fdopen() that calls register_file()
202
*/
203
204
FILE*
205
filefd(int fd, char* mode)
206
{
207
FILE *fp;
208
209
if (fp = fdopen(fd, mode)) {
210
register_file(fp, 0);
211
fcntl(fileno(fp), F_SETFD, 1);
212
}
213
return fp;
214
}
215
216
/*
217
* Generate temp file name.
218
* If fd>0 then create it and return file pointer.
219
* If fd>0 and buf==0 then remove after create.
220
*/
221
222
FILE*
223
filetemp(char* buf, int size, int type, int fd)
224
{
225
register char* s;
226
register char* b;
227
register char* e;
228
FILE* fp = 0;
229
230
if (!(b = buf)) {
231
if (fd <= 0)
232
return 0;
233
b = state.path.path;
234
size = sizeof(state.path.path);
235
}
236
e = b + size - 1;
237
s = strncopy(b, state.tmp.dir, e - b);
238
s = strncopy(s, "Mail", e - s);
239
if (s < e)
240
*s++ = type;
241
strncopy(s, "XXXXXX", e - s);
242
if (fd) {
243
fd = mkstemp(b);
244
if (!buf && *b)
245
remove(b);
246
if (fd < 0 || !(fp = filefd(fd, "r+"))) {
247
if (fd >= 0)
248
close(fd);
249
note(FATAL|SYSTEM|ERROR|IDENTIFY, "\"%s\": temporary file error", b);
250
}
251
}
252
else
253
mktemp(b);
254
if (!*b)
255
note(FATAL|SYSTEM|ERROR|IDENTIFY, "\"%s\": temporary file error", b);
256
return fp;
257
}
258
259
/*
260
* Call this after fileopen() or filefd().
261
*/
262
263
int
264
fileclose(FILE* fp)
265
{
266
int r;
267
struct file* p;
268
struct file** pp;
269
270
if (fp == 0 || fp == stdin)
271
return 0;
272
if (fp == stdout || fp == stderr)
273
return fflush(fp);
274
r = 0;
275
for (pp = &state.files;; pp = &p->link) {
276
if (!(p = *pp)) {
277
fclose(fp);
278
return 0;
279
}
280
if (p->fp == fp) {
281
r = p->pid;
282
*pp = p->link;
283
free(p);
284
break;
285
}
286
}
287
if (r) {
288
holdsigs();
289
fclose(fp);
290
signal(SIGPIPE, SIG_IGN);
291
r = wait_command(r);
292
signal(SIGPIPE, SIG_DFL);
293
relsesigs();
294
}
295
else
296
r = fclose(fp);
297
return r;
298
}
299
300
/*
301
* fileclose() all registered files.
302
*/
303
304
void
305
fileclear(void)
306
{
307
while (state.files)
308
fileclose(state.files->fp);
309
}
310
311
/*
312
* Copy n chars from from file ip to file op.
313
* If in is specified then input error messages enabled.
314
* If on is specified then output error messages enabled.
315
* If lines!=0 then it will point to the copied line count.
316
* If chars!=0 then it will point to the copied char count.
317
* If n==0 then all chars copied.
318
* If n>0 then exactly that many chars are copied.
319
*
320
* 0 returned on success.
321
*/
322
323
int
324
filecopy(const char* in, FILE* ip, const char* on, FILE* op, FILE* ap, register off_t n, register off_t* lines, off_t* chars, unsigned long flags)
325
{
326
register off_t c;
327
register char* s;
328
int r = 0;
329
off_t lc = 0;
330
off_t cc = 0;
331
char buf[LINESIZE + 1];
332
333
buf[sizeof(buf) - 1] = 0;
334
while (n >= 0) {
335
if ((c = fread(buf, 1, sizeof(buf) - 1, ip)) <= 0) {
336
if (c < 0) {
337
r = -1;
338
if (in) {
339
note(SYSTEM, "%s", in);
340
in = 0;
341
}
342
}
343
break;
344
}
345
if (n) {
346
if (n > c)
347
n -= c;
348
else {
349
c = n;
350
n = -1;
351
}
352
}
353
if (fwrite(buf, 1, c, op) != c) {
354
r = -1;
355
if (on) {
356
note(SYSTEM, "%s", on);
357
on = 0;
358
}
359
break;
360
}
361
cc += c;
362
if (ap)
363
fwrite(buf, 1, c, ap);
364
if (lines)
365
for (s = buf; s = strchr(s, '\n'); lc++, s++);
366
}
367
if (flags & GNL)
368
putc('\n', op);
369
if (fflush(op)) {
370
r = -1;
371
if (on)
372
note(SYSTEM, "%s", on);
373
}
374
if (n > 0) {
375
r = -1;
376
if (in)
377
note(SYSTEM, "%s", in);
378
}
379
if (lines)
380
*lines = lc;
381
if (chars)
382
*chars = cc;
383
return r;
384
}
385
386
/*
387
* Respond to a broken pipe signal --
388
* probably caused by quitting more.
389
*/
390
static void
391
sigpipe(int sig)
392
{
393
longjmp(state.jump.sigpipe, sig);
394
}
395
396
/*
397
* popen() via register_file()
398
*/
399
FILE*
400
pipeopen(char* cmd, char* mode)
401
{
402
int myside;
403
int hisside;
404
int fd0;
405
int fd1;
406
int pid;
407
FILE* fp;
408
int p[2];
409
int background = 0;
410
int jump = 0;
411
412
if (pipe(p) < 0)
413
goto bad;
414
fcntl(p[READ], F_SETFD, 1);
415
fcntl(p[WRITE], F_SETFD, 1);
416
for (;; mode++) {
417
switch (*mode) {
418
case 'J':
419
jump = 1;
420
continue;
421
case 'N':
422
background = 1;
423
continue;
424
}
425
break;
426
}
427
if (*mode == 'r') {
428
myside = p[READ];
429
fd0 = -1;
430
hisside = fd1 = p[WRITE];
431
}
432
else {
433
myside = p[WRITE];
434
hisside = fd0 = p[READ];
435
fd1 = -1;
436
}
437
if ((pid = start_command(state.var.shell, 0, fd0, fd1, "-c", cmd, NiL)) < 0) {
438
close(p[READ]);
439
close(p[WRITE]);
440
goto bad;
441
}
442
close(hisside);
443
if (!(fp = fdopen(myside, mode)))
444
goto bad;
445
register_file(fp, background ? 0 : pid);
446
if (jump && !background)
447
signal(SIGPIPE, sigpipe);
448
return fp;
449
bad:
450
note(SYSTEM, "\"%s\"", cmd);
451
return 0;
452
}
453
454
/*
455
* Run a command without a shell, with optional arguments and splicing
456
* of stdin and stdout. The command name can be a sequence of words.
457
* Signals must be handled by the caller.
458
* critical will mask interesting signals in the new process.
459
* SIGINT is enabled if critical==0.
460
*/
461
int
462
run_command(char* cmd, int critical, int infd, int outfd, char* a0, char* a1, char* a2)
463
{
464
int pid;
465
int code;
466
467
if ((pid = start_command(cmd, critical, infd, outfd, a0, a1, a2)) < 0)
468
return -1;
469
if (code = wait_command(pid)) {
470
note(SYSTEM, "Fatal exit code %d from %s", code, cmd);
471
return -1;
472
}
473
return 0;
474
}
475
476
extern pid_t spawnvp(const char*, char* const*); /* ast obsolete, but so is mailx, sort of */
477
478
int
479
start_command(char* cmd, int critical, int infd, int outfd, char* a0, char* a1, char* a2)
480
{
481
int pid;
482
int savein;
483
int saveout;
484
char** args;
485
char** p;
486
487
struct argvec vec;
488
489
if (!a0 && a1)
490
args = (char**)a1;
491
else {
492
initargs(&vec);
493
getargs(&vec, cmd);
494
if (a0) {
495
addarg(&vec, a0);
496
if (a1) {
497
addarg(&vec, a1);
498
if (a2)
499
addarg(&vec, a2);
500
}
501
}
502
endargs(&vec);
503
cmd = vec.argv[0];
504
args = vec.argv;
505
}
506
if (infd > READ) {
507
if ((savein = dup(READ)) < 0) {
508
note(SYSTEM, "%s: Cannot save standard input", cmd);
509
return -1;
510
}
511
fcntl(savein, F_SETFD, 1);
512
fcntl(infd, F_SETFD, 1);
513
close(READ);
514
if (dup(infd) != READ) {
515
note(SYSTEM, "%s: Cannot redirect standard input", cmd);
516
dup(savein);
517
close(savein);
518
return -1;
519
}
520
}
521
else infd = -1;
522
if (outfd > WRITE) {
523
if ((saveout = dup(WRITE)) < 0) {
524
note(SYSTEM, "%s: Cannot save standard output", cmd);
525
return -1;
526
}
527
fcntl(saveout, F_SETFD, 1);
528
fcntl(outfd, F_SETFD, 1);
529
close(WRITE);
530
if (dup(outfd) != WRITE) {
531
note(SYSTEM, "%s: Cannot redirect standard input", cmd);
532
dup(savein);
533
close(savein);
534
dup(saveout);
535
close(saveout);
536
return -1;
537
}
538
}
539
else
540
outfd = -1;
541
if (state.var.debug) {
542
note(DEBUG|PROMPT, "spawn:");
543
for (p = args; *p; p++)
544
printf(" \"%s\"", *p);
545
printf("\n");
546
}
547
if (critical)
548
sigcritical(critical);
549
if ((pid = spawnvp(cmd, args)) == -1)
550
note(SYSTEM, "%s", cmd);
551
if (critical)
552
sigcritical(0);
553
if (infd > READ) {
554
close(READ);
555
if (dup(savein) != READ) {
556
note(SYSTEM, "%s: Cannot restore standard input", cmd);
557
return -1;
558
}
559
close(savein);
560
fcntl(READ, F_SETFD, 0);
561
}
562
if (outfd > WRITE) {
563
close(WRITE);
564
if (dup(saveout) != WRITE) {
565
note(SYSTEM, "%s: Cannot restore standard output", cmd);
566
return -1;
567
}
568
close(saveout);
569
fcntl(WRITE, F_SETFD, 0);
570
}
571
return pid;
572
}
573
574
static struct child*
575
findchild(int pid)
576
{
577
register struct child** cpp;
578
579
for (cpp = &state.children; *cpp && (*cpp)->pid != pid;
580
cpp = &(*cpp)->link)
581
;
582
if (*cpp || (*cpp = (struct child*)malloc(sizeof(struct child)))) {
583
(*cpp)->pid = pid;
584
(*cpp)->done = (*cpp)->free = 0;
585
(*cpp)->link = 0;
586
}
587
return *cpp;
588
}
589
590
static void
591
delchild(register struct child* cp)
592
{
593
register struct child** cpp;
594
595
for (cpp = &state.children; *cpp != cp; cpp = &(*cpp)->link) ;
596
*cpp = cp->link;
597
free(cp);
598
}
599
600
/*
601
* Wait for a specific child to die.
602
*/
603
int
604
wait_command(int pid)
605
{
606
register struct child* cp = findchild(pid);
607
int status = -1;
608
609
holdsigs();
610
while (waitpid(pid, &status, 0) == -1 && errno == EINTR);
611
relsesigs();
612
delchild(cp);
613
return status;
614
}
615
616
/*
617
* Mark a command as don't care.
618
*/
619
void
620
free_command(int pid)
621
{
622
register struct child* cp = findchild(pid);
623
624
if (cp->done)
625
delchild(cp);
626
else
627
cp->free = 1;
628
}
629
630