chiark / gitweb /
close idle connections and spot unresponsive ones
[innduct.git] / backends / crosspost.c
1 /*  $Id: crosspost.c 6135 2003-01-19 01:15:40Z rra $
2 **
3 **  Parse input to add links for cross posted articles.  Input format is one
4 **  line per article.  Dots '.' are changed to '/'.  Commas ',' or blanks
5 **  ' ' separate entries.  Typically this is via a channel feed from innd
6 **  though an edit of the history file can also be used for recovery
7 **  purposes.  Sample newsfeeds entry:
8 **
9 **      # Create the links for cross posted articles
10 **      crosspost:*:Tc,Ap,WR:/usr/local/newsbin/crosspost
11 **
12 **  WARNING: This no longer works with the current INN; don't use it
13 **  currently.  It still exists in the source tree in case someone will
14 **  want to clean it up and make it useable again.
15 */
16
17 #include "config.h"
18 #include "clibrary.h"
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <syslog.h>
22 #include <sys/stat.h>
23
24 #include "inn/qio.h"
25 #include "libinn.h"
26 #include "paths.h"
27
28
29 static char     *Dir;
30
31 static int      debug = false;
32 static int      syncfiles = true;
33
34 static unsigned long STATTime    = 0;
35 static unsigned long STATMissing = 0; /* Source file missing */
36 static unsigned long STATTooLong = 0; /* Name Too Long (src or dest) */
37 static unsigned long STATLink    = 0; /* Link done */
38 static unsigned long STATLError  = 0; /* Link problem */
39 static unsigned long STATSymlink = 0; /* Symlink done */
40 static unsigned long STATSLError = 0; /* Symlink problem */
41 static unsigned long STATMkdir   = 0; /* Mkdir done */
42 static unsigned long STATMdError = 0; /* Mkdir problem */
43 static unsigned long STATOError  = 0; /* Other errors */
44
45 #define MAXXPOST 256
46 #define STATREFRESH 10800   /* 3 hours */
47
48 /*
49 **  Write some statistics and reset all counters.
50 */
51 void
52 ProcessStats()
53 {
54   time_t Time;
55
56   Time = time (NULL);
57   syslog(L_NOTICE,
58         "seconds %lu links %lu %lu symlinks %lu %lu mkdirs %lu %lu missing %lu toolong %lu other %lu",
59         Time - STATTime, STATLink, STATLError, STATSymlink, STATSLError,
60         STATMkdir, STATMdError, STATMissing, STATTooLong, STATOError);
61
62   STATMissing = STATTooLong = STATLink = STATLError = 0;
63   STATSymlink = STATSLError = STATMkdir = STATMdError = STATOError = 0;
64   STATTime = Time;
65 }
66
67 /*
68 **  Try to make one directory.  Return false on error.
69 */
70 static bool
71 MakeDir(Name)
72     char                *Name;
73 {
74     struct stat         Sb;
75
76     if (mkdir(Name, GROUPDIR_MODE) >= 0) {
77         STATMkdir++;
78         return true;
79     }
80
81     /* See if it failed because it already exists. */
82     return stat(Name, &Sb) >= 0 && S_ISDIR(Sb.st_mode);
83 }
84
85
86 /*
87 **  Make spool directory.  Return false on error.
88 */
89 static bool
90 MakeSpoolDir(Name)
91     char        *Name;
92 {
93     char        *p;
94     bool                made;
95
96     /* Optimize common case -- parent almost always exists. */
97     if (MakeDir(Name))
98         return true;
99
100     /* Try to make each of comp and comp/foo in turn. */
101     for (p = Name; *p; p++)
102         if (*p == '/') {
103             *p = '\0';
104             made = MakeDir(Name);
105             *p = '/';
106             if (!made) {
107                 STATMdError++;
108                 return false;
109             }
110         }
111
112     return MakeDir(Name);
113 }
114
115
116 /*
117 **  Process the input.  Data can come from innd:
118 **      news/group/name/<number> [space news/group/<number>]...
119 **  or
120 **      news.group.name/<number>,[news.group.name/<number>]...
121 */
122 static void
123 ProcessIncoming(qp)
124     QIOSTATE            *qp;
125 {
126     char        *p;
127     int i;
128     int                 nxp;
129     int                 fd;
130     int                 lnval ;
131     char        *names[MAXXPOST];
132
133
134     for ( ; ; ) {
135
136         if (time(NULL) - STATTime > STATREFRESH)
137           ProcessStats();
138
139         /* Read the first line of data. */
140         if ((p = QIOread(qp)) == NULL) {
141             if (QIOtoolong(qp)) {
142                 fprintf(stderr, "crosspost line too long\n");
143                 STATTooLong++;
144                 continue;
145             }
146             break;
147         }
148
149         for (i = 0; *p && (i < MAXXPOST); i++) { /* parse input into array */
150             names[i] = p;
151             for ( ; *p; p++) {
152                 if (*p == '.') *p++ = '/'; /* dot to slash translation */
153                 else if ((*p == ',')       /* name separators */
154                   ||     (*p == ' ')
155                   ||     (*p == '\t')
156                   ||     (*p == '\n')) {
157                     *p++ = '\0';
158                     break;
159                 }
160             }
161         }
162         nxp = i;
163         if (debug) {
164             for (i = 0; i < nxp; i++)
165                 fprintf(stderr, "crosspost: debug %d %s\n",
166                     i, names[i]);
167         }
168
169         if(syncfiles) fd = open(names[0], O_RDWR);
170
171         for (i = 1; i < nxp; i++) {
172             lnval = link(names[0], names[i]) ;
173             if (lnval == 0) STATLink++;
174             if (lnval < 0 && errno != EXDEV) { /* first try to link */
175                 int j;
176                 char path[SPOOLNAMEBUFF+2];
177
178                 for (j = 0; (path[j] = names[i][j]) != '\0' ; j++) ;
179                 for (j--; (j > 0) && (path[j] != '/'); j--) ;
180                 if (path[j] == '/') {
181                     path[j] = '\0';
182                     /* try making parent dir */
183                     if (MakeSpoolDir(path) == false) {
184                         fprintf(stderr, "crosspost cant mkdir %s\n",
185                                 path);
186                     }
187                     else {
188                         /* 2nd try to link */
189                         lnval = link(names[0], names[i]) ;
190                         if (lnval == 0) STATLink++;
191                         if (lnval < 0 && errno == EXDEV) {
192 #if !defined(HAVE_SYMLINK)
193                             fprintf(stderr, "crosspost cant link %s %s",
194                                 names[0], names[i]);
195                             perror(" ");
196 #else
197                             /* Try to make a symbolic link
198                             ** to the full pathname.
199                             */
200                             for (j = 0, p = Dir; (j < SPOOLNAMEBUFF) && *p; )
201                                 path[j++] = *p++; /* copy spool dir */
202                             if (j < SPOOLNAMEBUFF) path[j++] = '/';
203                             for (p = names[0]; (j < SPOOLNAMEBUFF) && *p; )
204                                 path[j++] = *p++;       /* append path */
205                             path[j++] = '\0';
206                             if (symlink(path, names[i]) < 0) {
207                                 fprintf(stderr,
208                                     "crosspost cant symlink %s %s",
209                                     path, names[i]);
210                                 perror(" ");
211                                 STATSLError++;
212                             }
213                             else
214                               STATSymlink++;
215 #endif  /* !defined(HAVE_SYMLINK) */
216                         } else if (lnval < 0) {
217                             if (lnval == ENOENT)
218                               STATMissing++;
219                             else {
220                               fprintf(stderr, "crosspost cant link %s %s",
221                                             names[0], names[i]);
222                               perror(" ");
223                               STATLError++;
224                             }
225                         }
226                     }
227                 } else {
228                     fprintf(stderr, "crosspost bad path %s\n",
229                             names[i]);
230                     STATOError++;
231                 }
232             } else if (lnval < 0) {
233                 int j;
234                 char path[SPOOLNAMEBUFF+2];
235
236 #if !defined(HAVE_SYMLINK)
237                 fprintf(stderr, "crosspost cant link %s %s",
238                               names[0], names[i]);
239                 perror(" ");
240 #else
241                 /* Try to make a symbolic link
242                 ** to the full pathname.
243                 */
244                 for (j = 0, p = Dir; (j < SPOOLNAMEBUFF) && *p; )
245                     path[j++] = *p++; /* copy spool dir */
246                 if (j < SPOOLNAMEBUFF) path[j++] = '/';
247                 for (p = names[0]; (j < SPOOLNAMEBUFF) && *p; )
248                     path[j++] = *p++;   /* append path */
249                 path[j++] = '\0';
250                 if (symlink(path, names[i]) < 0) {
251                     fprintf(stderr,
252                                   "crosspost cant symlink %s %s",
253                                   path, names[i]);
254                     perror(" ");
255                     STATSLError++;
256                 }
257                 else
258                   STATSymlink++;
259 #endif  /* !defined(HAVE_SYMLINK) */
260             }
261         }
262
263         if (syncfiles && (fd >= 0)) {
264             fsync(fd);
265             close(fd);
266         }
267     }
268
269     if (QIOerror(qp))
270         fprintf(stderr, "crosspost cant read %s\n", strerror(errno));
271     QIOclose(qp);
272 }
273
274
275 static void
276 Usage(void)
277 {
278     fprintf(stderr, "usage:  crosspost [-D dir] [files...]\n");
279     exit(1);
280 }
281
282
283 int
284 main(ac, av)
285     int                 ac;
286     char                *av[];
287 {
288     int i;
289     QIOSTATE            *qp;
290
291     /* Set defaults. */
292     if (ReadInnConf() < 0) exit(1);
293     Dir = innconf->patharticles;
294     umask(NEWSUMASK);
295
296     /* Parse JCL. */
297     while ((i = getopt(ac, av, "D:ds")) != EOF)
298         switch (i) {
299         default:
300             Usage();
301             /* NOTREACHED */
302         case 'D':
303             Dir = optarg;       /* specify spool path */
304             break;
305         case 'd':
306             debug = true;
307             break;
308         case 's':
309             syncfiles = false;  /* do not fsync articles */
310             break;
311         }
312     ac -= optind;
313     av += optind;
314
315     if (chdir(Dir) < 0) {
316         fprintf(stderr, "crosspost cant chdir %s %s\n",
317                 Dir, strerror(errno));
318         exit(1);
319     }
320     openlog("crosspost", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
321     STATTime = time (NULL);
322     if (ac == 0)
323         ProcessIncoming(QIOfdopen(STDIN_FILENO));
324     else {
325         for ( ; *av; av++)
326             if (strcmp(*av, "-") == 0)
327                 ProcessIncoming(QIOfdopen(STDIN_FILENO));
328             else if ((qp = QIOopen(*av)) == NULL)
329                 fprintf(stderr, "crosspost cant open %s %s\n",
330                         *av, strerror(errno));
331             else
332                 ProcessIncoming(qp);
333     }
334
335     ProcessStats();
336     exit(0);
337     /* NOTREACHED */
338 }