chiark / gitweb /
Strip redundant Emacs mode markers from Perl scripts.
[distorted-backup] / rmt.c
CommitLineData
99248ed2
MW
1/* -*-c-*-
2 *
3 * Fake rmt(8) server for hashing and storing files
4 *
5 * (c) 2010 Mark Wooding
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25/*----- Header files ------------------------------------------------------*/
26
27#define _FILE_OFFSET_BITS 64
28
29#include <ctype.h>
30#include <errno.h>
31#include <limits.h>
32#include <signal.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
38#include <unistd.h>
39#include <fcntl.h>
40
41#include <getopt.h>
42
43#include <pwd.h>
44
45#include <mLib/dstr.h>
46#include <mLib/quis.h>
47#include <mLib/report.h>
48
49#include <nettle/sha.h>
50
51/*----- Configuration -----------------------------------------------------*/
52
53#ifndef BKP
54# define BKP "/mnt/bkp"
55#endif
56
57/*----- Main code ---------------------------------------------------------*/
58
59#define BUFSZ 10240
60#define LSEEK_GET_TAPEPOS 10
61#define LSEEK_GO2_TAPEPOS 11
62
63struct flag {
64 const char *name;
65 int f;
66};
67
68static const struct flag openflag[] = {
69 /* ;;; Emacs Lisp to generate the table below. Place your cursor just
70 ;;; after the closing `)' and press C-x C-e.
71
72 (let ((flags '(rdonly wronly rdwr creat excl trunc nonblock ndelay
73 noctty append dsync rsync sync cloexec async
74 direct noatime nofollow shlock exlock defer)))
75 (save-excursion
76 (goto-char (point-min))
77 (search-forward (concat "***" "BEGIN openflag" "***"))
78 (beginning-of-line 2)
79 (delete-region (point)
80 (progn
81 (search-forward "***END***")
82 (beginning-of-line)
83 (point)))
84 (dolist (f (sort (copy-list flags) #'string<))
85 (let ((up (upcase (symbol-name f))))
86 (insert (format "#ifdef O_%s\n" up))
87 (insert (format " { \"%s\", O_%s },\n" up up))
88 (insert "#endif\n")))))
89 */
90 /***BEGIN openflag***/
91#ifdef O_APPEND
92 { "APPEND", O_APPEND },
93#endif
94#ifdef O_ASYNC
95 { "ASYNC", O_ASYNC },
96#endif
97#ifdef O_CLOEXEC
98 { "CLOEXEC", O_CLOEXEC },
99#endif
100#ifdef O_CREAT
101 { "CREAT", O_CREAT },
102#endif
103#ifdef O_DEFER
104 { "DEFER", O_DEFER },
105#endif
106#ifdef O_DIRECT
107 { "DIRECT", O_DIRECT },
108#endif
109#ifdef O_DSYNC
110 { "DSYNC", O_DSYNC },
111#endif
112#ifdef O_EXCL
113 { "EXCL", O_EXCL },
114#endif
115#ifdef O_EXLOCK
116 { "EXLOCK", O_EXLOCK },
117#endif
118#ifdef O_NDELAY
119 { "NDELAY", O_NDELAY },
120#endif
121#ifdef O_NOATIME
122 { "NOATIME", O_NOATIME },
123#endif
124#ifdef O_NOCTTY
125 { "NOCTTY", O_NOCTTY },
126#endif
127#ifdef O_NOFOLLOW
128 { "NOFOLLOW", O_NOFOLLOW },
129#endif
130#ifdef O_NONBLOCK
131 { "NONBLOCK", O_NONBLOCK },
132#endif
133#ifdef O_RDONLY
134 { "RDONLY", O_RDONLY },
135#endif
136#ifdef O_RDWR
137 { "RDWR", O_RDWR },
138#endif
139#ifdef O_RSYNC
140 { "RSYNC", O_RSYNC },
141#endif
142#ifdef O_SHLOCK
143 { "SHLOCK", O_SHLOCK },
144#endif
145#ifdef O_SYNC
146 { "SYNC", O_SYNC },
147#endif
148#ifdef O_TRUNC
149 { "TRUNC", O_TRUNC },
150#endif
151#ifdef O_WRONLY
152 { "WRONLY", O_WRONLY },
153#endif
154 /***END***/
155 { 0, 0 }
156};
157
158int main(int argc, char *argv[])
159{
160 int ch;
161 dstr d = DSTR_INIT, dd = DSTR_INIT;
162 unsigned char buf[BUFSZ];
163 int fd = -1, hfd = -1;
164 off_t rc;
165 off_t off = 0;
166 struct passwd *pw;
167 uid_t u;
168 unsigned f = 0;
169#define f_bogus 1u
170 const char *p = 0;
171 const char *bkp = 0, *host = 0;
172 struct sha256_ctx hc;
173
174 ego(argv[0]);
175 setvbuf(stdin, 0, _IONBF, 0);
176 signal(SIGPIPE, SIG_IGN);
177
178 for (;;) {
179 int o = getopt(argc, argv, "H:r:");
180 if (o < 0) break;
181 switch (o) {
182 case 'H': host = optarg; break;
183 case 'r': bkp = optarg; break;
184 default: f |= f_bogus; break;
185 }
186 }
187 argc -= optind; argv += optind;
188 if ((f & f_bogus) || argc) {
189 pquis(stderr, "usage: $ [-r ROOT] [-H HOST]\n");
190 exit(1);
191 }
192
193 if (!bkp) bkp = getenv("BKP");
194 if (!bkp) bkp = BKP;
195
196 if (!host) host = getenv("BKP_HOST");
197
198 if (!host) {
199 p = getenv("USER");
200 if (!p) p = getenv("LOGNAME");
201 if (!p) {
202 u = getuid();
203 if ((pw = getpwuid(u)) == 0)
204 die(1, "no passwd entry (you don't exist?)");
205 p = pw->pw_name;
206 }
207 if (strncmp(p, "bkp-", 4) != 0) {
208 die(1, "can't deduce host name: "
209 "login name `%s' doesn't begin with `bkp-'",
210 p);
211 }
212 host = p + 4;
213 }
214
215 for (;;) {
216 if (fflush(stdout)) goto fail;
217 ch = getchar();
218 if (ch == EOF) break;
219 DRESET(&d); DRESET(&dd); rc = 0;
220
221#define CHECKFD do { if (fd < 0) goto badf; } while (0)
222#define ERROR(what) do moan(what ": %s", strerror(errno)); while (0)
223#define ERROR1(what, arg) \
224 do moan(what ": %s", arg, strerror(errno)); while (0)
225#define ARG(d) do { \
226 if (dstr_putline(&d, stdin) == EOF || ferror(stdin) || feof(stdin)) \
227 { moan("read (stdin)", strerror(errno)); goto fail; } \
228} while (0)
229
230#define SKIPWS do { while (isspace((unsigned char)*p)) p++; } while (0)
231
232 switch (ch) {
233
234 case 'O': {
235 /* Ofile\nmode\n -- open file */
236
237 const struct flag *ff;
238 char *p, *q;
239 size_t n;
240 long mode, f;
241
242 ARG(d); ARG(dd);
243 if (fd >= 0 && close(fd)) ERROR("close (fd)");
244 if (hfd >= 0 && close(hfd)) ERROR("close (hash)");
245 fd = hfd = -1;
246
247 if (chdir(bkp) || chdir(host)) ERROR1("chdir (%s)", host);
248 p = d.buf;
249 if ((q = strchr(p, '/')) == 0)
250 { moan("bad path: missing `/')"); goto inval; }
251 *q++ = 0;
252 if (chdir(p) || chdir("prepare/incoming")) ERROR1("chdir (%s)", p);
253
254 memmove(d.buf, q, d.len - (q - d.buf) + 1);
255 d.len -= q - d.buf;
256
257 errno = 0;
258 mode = strtol(dd.buf, &p, 0);
259 if (errno) ERROR("bad mode");
260 else if (mode < 0 || mode > INT_MAX)
261 { moan("bad mode: range"); goto range; }
262 SKIPWS;
263 if (!*p) {
264 switch (mode & O_ACCMODE) {
265 case O_RDONLY: mode = O_RDONLY; break;
266 case O_WRONLY: mode = O_WRONLY | O_TRUNC | O_CREAT; break;
267 case O_RDWR: mode = O_RDWR;
268 default: moan("bad mode: unknown access type"); goto inval;
269 }
270 } else {
271 mode = 0;
272 for (;;) {
273 if (p[0] == 'O' && p[1] == '_') {
274 p += 2;
275 n = strcspn(p, " \t|");
276 for (ff = openflag; ff->name; ff++) {
277 if (strncmp(p, ff->name, n) == 0 && !ff->name[n])
278 goto ofmatch;
279 }
280 moan("bad mode: unknown flag O_%.*s", (int)n, p);
281 goto inval;
282 ofmatch:
283 mode |= ff->f;
284 p += n;
285 } else if (isdigit((unsigned long)*p)) {
286 errno = 0;
287 f = strtol(p, &p, 0);
288 if (errno) ERROR("bad mode");
289 else if (f < 0 || f > INT_MAX)
290 { moan("bad mode: range"); goto range; }
291 mode |= f;
292 } else {
293 moan("bad mode: unexpected token");
294 goto inval;
295 }
296 SKIPWS;
297 if (!*p)
298 break;
299 else if (*p != '|') {
300 moan("bad mode: expected `|'");
301 goto inval;
302 }
303 p++;
304 SKIPWS;
305 }
306 }
307
308 if ((fd = open(d.buf, mode, 0666)) < 0) ERROR1("open (%s)", d.buf);
309 if ((mode & O_ACCMODE) == O_WRONLY) {
310 DPUTS(&d, ".hash");
311 if ((hfd = open(d.buf,
312 mode & (O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC),
313 0666)) < 0) {
314 close(fd); fd = -1;
315 ERROR1("open (%s)", d.buf);
316 }
317 sha256_init(&hc);
318 }
319 off = 0;
320 } break;
321
322 case 'C': {
323 /* Chunoz\n -- close file */
324
325 uint8_t h[SHA256_DIGEST_SIZE], *p;
326 char hex[SHA256_DIGEST_SIZE * 2 + 1], *q;
327 unsigned i;
328
329 ARG(d); CHECKFD;
330 if (close(fd)) ERROR("close (fd)");
331 fd = -1;
332 if (hfd >= 0) {
333 sha256_digest(&hc, sizeof(h), h);
334 for (p = h, q = hex; p < h + sizeof(h); p++, q += 2)
335 sprintf(q, "%02x", *p);
336 *q++ = '\n';
337 errno = EIO;
338 if (write(hfd, hex, sizeof(hex)) < sizeof(hex) || close(hfd))
339 ERROR("close (hash)");
340 hfd = -1;
341 }
342 } break;
343
344 case 'L': {
345 /* Loffset\nwhence\n -- seek
346 * (warning: the manual page gets these the wrong way round)
347 */
348
349 int whence;
350 off_t offset;
351
352 ARG(d); ARG(dd); CHECKFD;
353 offset = atoi(d.buf); whence = strtoull(dd.buf, 0, 0);
354 switch (whence) {
355 case LSEEK_GET_TAPEPOS: whence = SEEK_CUR; offset = 0; break;
356 case LSEEK_GO2_TAPEPOS: whence = SEEK_SET; break;
357 }
358 switch (whence) {
359 case SEEK_CUR:
360 if (!offset) { rc = off; break; }
361 default:
362 rc = lseek(fd, offset, whence);
363 if (rc == (off_t)-1) ERROR("seek");
364 off = rc;
365 break;
366 }
367 } break;
368
369 case 'W': {
370 /* Wlen\ndata... -- write */
371
372 size_t n, nn, sz;
373 ssize_t ssz;
374 unsigned char *p;
375 int botch = 0;
376
377 ARG(d); CHECKFD;
378 rc = sz = strtoul(d.buf, 0, 0);
379 while (sz) {
380 nn = sz > BUFSZ ? BUFSZ : sz;
381 n = fread(buf, 1, nn, stdin);
382 if (n < nn) {
383 if (feof(stdin)) { moan("eof on stdin"); goto fail;}
384 else ERROR("read (stdin)");
385 }
386 if (hfd >= 0) sha256_update(&hc, n, buf);
387 p = buf;
388 while (!botch && n) {
389 if ((ssz = write(fd, p, n)) > 0) {
390 p += ssz; off += ssz; n -= ssz;
391 } else if (!ssz) { moan("zero-length write"); goto fail; }
392 else if (errno != EINTR) { botch = errno; }
393 }
394 sz -= nn;
395 }
396 if (botch) { errno = botch; ERROR("write"); }
397 } break;
398
399 case 'R': {
400 /* Rlen\n -- read */
401
402 size_t nn;
403 ssize_t ssz;
404
405 ARG(d); CHECKFD;
406 nn = strtoul(d.buf, 0, 0); if (nn > BUFSZ) nn = BUFSZ;
407 if ((ssz = read(fd, buf, nn)) < 0) ERROR("read");
408 off += ssz;
409 printf("A%ld\n", (long)ssz);
410 if (fwrite(buf, 1, ssz, stdout) < ssz)
411 { moan("write (stdout): %s", strerror(errno)); goto fail; }
412 continue;
413 } break;
414
415 case 'i': case 'I':
416 /* Iop\ncount\n -- ioctl */
417 ARG(d); ARG(dd); CHECKFD; goto notty;
418
419 case 'S':
420 /* S -- ioctl */
421 CHECKFD; goto notty;
422 case 's':
423 /* sop -- ioctl */
424
425 if ((ch = getchar()) == EOF) goto fail;
426 CHECKFD; goto notty;
427
428 default:
429 goto fail;
430 }
431
432 printf("A%llu\n", (unsigned long long)rc);
433 continue;
434
435 badf: errno = EBADF; goto error;
436 range: errno = ERANGE; goto error;
437 inval: errno = EINVAL; goto error;
438 notty: errno = ENOTTY; goto error;
439 error:
440 printf("E%d\n%s\n", errno, strerror(errno));
441 continue;
442 }
443 if (fflush(stdout) || ferror(stdout) || ferror(stdin)) goto fail;
444 return (0);
445fail:
446 return (1);
447}
448
449/*----- That's all, folks -------------------------------------------------*/