Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / miscellany / sendmail / spooler.c
1 /* spooler.c --  MH style mailer to write to /usr/spool/mail/<user>
2  *
3  *     Mlocal, P=/etc/spooler,  F=lsDFSmn, S=10, R=20, A=spooler $u
4  */
5
6
7 #define FLOCK   /* uses flock() if defined, lockf() otherwise. */
8
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/file.h>
13 #include <pwd.h>
14 #include "sysexits.h"
15
16 #define SPOOLDIR "/usr/spool/mail"
17
18 char *delim = "\1\1\1\1\n";
19 char *ME = "/etc/spooler";
20
21 FILE *mboxopen();
22 FILE *fp_in;
23
24 main( argc, argv )
25 int argc;
26 char **argv;
27 {
28     register int rc, anyerrs;
29     char tmpfile[100];
30
31     if( *++argv == NULL ) {
32         fprintf( stderr, "%s: no recipients.\n", ME );
33         exit( EX_USAGE );
34     }
35
36     if( argc == 2 ) {           /* single recipient, don't need tmp copy */
37         fp_in = stdin;
38         rc = localmail( *argv );
39     }
40     else {
41         if(( rc = copyfile( tmpfile )) == 0 ) {     /* sets fp_in */
42             for( anyerrs = 0; *argv; argv++ ) {
43                 rewind( fp_in );
44                 if(( rc = localmail( *argv )))
45                     anyerrs++;
46             }
47             if( !anyerrs )
48                 unlink( tmpfile );
49         }
50     }
51
52     exit( rc );
53 }
54
55
56
57 localmail( user )
58 char   *user;
59 {
60     register FILE *fp;
61     register int count, n;
62     register char buf[BUFSIZ];
63     register char mailbox[BUFSIZ];
64     register struct stat sb;
65
66     sprintf( mailbox, "%s/%s", SPOOLDIR, user );
67
68     if( stat( mailbox, &sb ) == -1 ) {
69         if( create_mbox( mailbox, user ))
70             return(EX_TEMPFAIL);
71     }
72
73     if(( fp = mboxopen( mailbox )) == 0 )
74         return(EX_TEMPFAIL);
75     fwrite( delim, sizeof(char), strlen(delim), fp );
76     while(( count = fread( buf, sizeof(char), BUFSIZ, fp_in))) {
77         n = fwrite( buf, sizeof(char), count, fp );
78         if( n != count ) {
79             fprintf( stderr, "%s write error: %d read, %d written\n",
80                 ME, count, n );
81             mboxclose(fp);
82             return(-1);
83         }
84     }
85     fwrite( delim, sizeof(char), strlen(delim), fp );
86     mboxclose(fp);
87
88     return(0);
89 }
90
91
92 create_mbox( mbox, user )
93 register char *mbox, *user;
94 {
95     register int fd;
96     register struct passwd *pw;
97
98     if(( fd = creat( mbox, 0600 )) == -1 )
99         return -1;
100     if(( pw = getpwnam( user )) != NULL ) {
101         fchown( fd, pw->pw_uid, pw->pw_gid );
102     }
103     close( fd );
104
105     return 0;
106 }
107
108
109 FILE *
110 mboxopen( mailbox )
111 register char *mailbox;
112 {
113     register FILE *fp;
114     register int i;
115
116     if(( fp = fopen( mailbox, "a" )) == NULL ) {
117         fprintf( stderr, "%s:  Can't open %s for appending\n", ME, mailbox );
118         return(0);
119     }
120
121     /* lock the mailbox */
122
123 #define NLOCKTRYS 20        /* almost a 2 min. wait should be enough even */
124                             /* after a long vacation */
125
126     for( i = 0; i < NLOCKTRYS; i++ ) {
127 #ifdef FLOCK
128         if( flock( fileno(fp), LOCK_EX | LOCK_NB ) == 0)    /* got it ok */
129 #else
130         if( lockf( fileno(fp), F_TLOCK, 0) == 0 )
131 #endif FLOCK
132             break;
133         /* fprintf(stderr, "can't lock, sleeping 5 sec...\n"); */
134         sleep(5);
135     }
136 /*
137  *  If lockf worked perfectly (ie, rpc.lockd didn't die) this would be
138  *  just fine.
139  */
140 #ifdef DontDoThis
141     if( i == NLOCKTRYS ) {
142         fprintf( stderr, "%s:  Can't lock %s\n", ME, mailbox);
143         return(0);
144     }
145 #endif
146
147     fseek( fp, 0, 2 );
148
149     return(fp);
150 }
151
152
153 mboxclose(fp)
154 FILE *fp;
155 {
156 /*todo: unlockit.  --not really necessary */
157     fclose(fp);
158 }
159
160
161
162 /*
163  *  collect stdin to a tmp file
164  */
165 copyfile( tmpfile )
166 register char *tmpfile;
167 {
168     struct stat sb;
169     register int pid, count, n;
170     register char buf[BUFSIZ];
171     register FILE *fp;
172
173     pid = getpid();
174
175     while( 1 ) {
176         sprintf( tmpfile, "/tmp/spooler.%05d", pid );
177         if( stat( tmpfile, &sb ) == -1 )    /* ok, this file doesn't exist */
178             break;
179         pid++;
180     }
181
182     if(( fp = fopen( tmpfile, "w" )) == NULL ) {
183         fprintf( stderr, "%s:  Can't open %s\n", ME, tmpfile );
184         return(EX_TEMPFAIL);
185     }
186
187     while(( count = fread( buf, sizeof(char), BUFSIZ, stdin ))) {
188         n = fwrite( buf, sizeof(char), count, fp );
189         if( n != count ) {
190             fprintf( stderr, "%s: copyfile, write error: %d read, %d written\n",
191                 ME, count, n );
192             fclose(fp);
193             return(-1);
194         }
195     }
196
197     fp_in = freopen( tmpfile, "r", fp );
198     if( fp_in == NULL ) {
199         fprintf( stderr, "%s: freopen on %s failed\n", ME, tmpfile );
200         return(-1);
201     }
202
203     return(0);
204 }