chiark / gitweb /
poke => prod/next
[inn-innduct.git] / backends / filechan.c
1 /*  $Id: filechan.c 6135 2003-01-19 01:15:40Z rra $
2 **
3 **  An InterNetNews channel process that splits a funnel entry into
4 **  separate files.  Originally from Robert Elz <kre@munnari.oz.au>.
5 */
6
7 #include "config.h"
8 #include "clibrary.h"
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/stat.h>
12
13 #include "inn/innconf.h"
14 #include "inn/messages.h"
15 #include "libinn.h"
16 #include "paths.h"
17
18 #include "map.h"
19
20 int
21 main(int ac, char *av[])
22 {
23     char                buff[2048];
24     char                *p;
25     char                *next;
26     int                 i;
27     int                 fd;
28     int                 Fields;
29     const char          *Directory;
30     bool                Map;
31     FILE                *F;
32     struct stat         Sb;
33     uid_t               uid;
34     gid_t               gid;
35     uid_t               myuid;
36
37     /* First thing, set up our identity. */
38     message_program_name = "filechan";
39
40     /* Set defaults. */
41     if (!innconf_read(NULL))
42         exit(1);
43     Fields = 1;
44     Directory = innconf->pathoutgoing;
45     Map = false;
46     myuid = geteuid();
47     umask(NEWSUMASK);
48
49     /* Parse JCL. */
50     while ((i = getopt(ac, av, "d:f:m:p:")) != EOF)
51         switch (i) {
52         default:
53             die("usage error");
54             break;
55         case 'd':
56             Directory = optarg;
57             break;
58         case 'f':
59             Fields = atoi(optarg);
60             break;
61         case 'm':
62             Map = true;
63             MAPread(optarg);
64             break;
65         case 'p':
66             if ((F = fopen(optarg, "w")) == NULL)
67                 sysdie("cannot fopen %s", optarg);
68             fprintf(F, "%ld\n", (long)getpid());
69             if (ferror(F) || fclose(F) == EOF)
70                 sysdie("cannot fclose %s", optarg);
71             break;
72         }
73
74     /* Move, and get owner of current directory. */
75     if (chdir(Directory) < 0)
76         sysdie("cannot chdir to %s", Directory);
77     if (stat(".", &Sb) < 0)
78         sysdie("cannot stat %s", Directory);
79     uid = Sb.st_uid;
80     gid = Sb.st_gid;
81
82     /* Read input. */
83     while (fgets(buff, sizeof buff, stdin) != NULL) {
84         if ((p = strchr(buff, '\n')) != NULL)
85             *p = '\0';
86
87         /* Skip the right number of leading fields. */
88         for (i = Fields, p = buff; *p; p++)
89             if (*p == ' ' && --i <= 0)
90                 break;
91         if (*p == '\0')
92             /* Nothing to write.  Probably shouldn't happen. */
93             continue;
94
95         /* Add a newline, get the length of all leading fields. */
96         *p++ = '\n';
97         i = p - buff;
98
99         /* Rest of the line is space-separated list of filenames. */
100         for (; *p; p = next) {
101             /* Skip whitespace, get next word. */
102             while (*p == ' ')
103                 p++;
104             for (next = p; *next && *next != ' '; next++)
105                 continue;
106             if (*next)
107                 *next++ = '\0';
108
109             if (Map)
110                 p = MAPname(p);
111             fd = open(p, O_CREAT | O_WRONLY | O_APPEND, BATCHFILE_MODE);
112             if (fd >= 0) {
113                 /* Try to lock it and set the ownership right. */
114                 inn_lock_file(fd, INN_LOCK_WRITE, true);
115                 if (myuid == 0 && uid != 0)
116                     chown(p, uid, gid);
117
118                 /* Just in case, seek to the end. */
119                 lseek(fd, 0, SEEK_END);
120
121                 errno = 0;
122                 if (write(fd, buff, i) != i)
123                     sysdie("write failed");
124
125                 close(fd);
126             }
127         }
128     }
129
130     exit(0);
131     /* NOTREACHED */
132 }