1/*
2   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13*/
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif /* HAVE_CONFIG_H */
18
19#include <unistd.h>
20#include <sys/types.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <stdarg.h>
24#include <limits.h>
25#include <signal.h>
26#include <string.h>
27#include <errno.h>
28
29#include <atalk/logger.h>
30#include <atalk/globals.h>
31#include <atalk/netatalk_conf.h>
32#include <atalk/util.h>
33#include <atalk/errchk.h>
34
35#include "cmd_dbd.h"
36
37enum dbd_cmd {dbd_scan, dbd_rebuild};
38
39/* Global variables */
40volatile sig_atomic_t alarmed;  /* flags for signals */
41
42/* Local variables */
43static dbd_flags_t flags;
44
45/***************************************************************************
46 * Local functions
47 ***************************************************************************/
48
49/*
50 * SIGNAL handling:
51 * catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
52 */
53static void sig_handler(int signo)
54{
55    alarmed = 1;
56    return;
57}
58
59static void set_signal(void)
60{
61    struct sigaction sv;
62
63    sv.sa_handler = sig_handler;
64    sv.sa_flags = SA_RESTART;
65    sigemptyset(&sv.sa_mask);
66    if (sigaction(SIGTERM, &sv, NULL) < 0) {
67        dbd_log( LOGSTD, "error in sigaction(SIGTERM): %s", strerror(errno));
68        exit(EXIT_FAILURE);
69    }
70    if (sigaction(SIGINT, &sv, NULL) < 0) {
71        dbd_log( LOGSTD, "error in sigaction(SIGINT): %s", strerror(errno));
72        exit(EXIT_FAILURE);
73    }
74
75    memset(&sv, 0, sizeof(struct sigaction));
76    sv.sa_handler = SIG_IGN;
77    sigemptyset(&sv.sa_mask);
78
79    if (sigaction(SIGABRT, &sv, NULL) < 0) {
80        dbd_log( LOGSTD, "error in sigaction(SIGABRT): %s", strerror(errno));
81        exit(EXIT_FAILURE);
82    }
83    if (sigaction(SIGHUP, &sv, NULL) < 0) {
84        dbd_log( LOGSTD, "error in sigaction(SIGHUP): %s", strerror(errno));
85        exit(EXIT_FAILURE);
86    }
87    if (sigaction(SIGQUIT, &sv, NULL) < 0) {
88        dbd_log( LOGSTD, "error in sigaction(SIGQUIT): %s", strerror(errno));
89        exit(EXIT_FAILURE);
90    }
91}
92
93static void usage (void)
94{
95    printf("Usage: dbd [-cfFstvV] <path to netatalk volume>\n\n"
96           "dbd scans all file and directories of AFP volumes, updating the\n"
97           "CNID database of the volume. dbd must be run with appropiate\n"
98           "permissions i.e. as root.\n\n"
99           "Options:\n"
100           "   -s scan volume: treat the volume as read only and don't\n"
101           "      perform any filesystem modifications\n"
102           "   -c convert from adouble:v2 to adouble:ea\n"
103           "   -F location of the afp.conf config file\n"
104           "   -f delete and recreate CNID database\n"
105           "   -t show statistics while running\n"
106           "   -v verbose\n"
107           "   -V show version info\n\n"
108        );
109}
110
111/***************************************************************************
112 * Global functions
113 ***************************************************************************/
114
115void dbd_log(enum logtype lt, char *fmt, ...)
116{
117    int len;
118    static char logbuffer[1024];
119    va_list args;
120
121    if ( (lt == LOGSTD) || (flags & DBD_FLAGS_VERBOSE)) {
122        va_start(args, fmt);
123        len = vsnprintf(logbuffer, 1023, fmt, args);
124        va_end(args);
125        logbuffer[1023] = 0;
126
127        printf("%s\n", logbuffer);
128    }
129}
130
131int main(int argc, char **argv)
132{
133    EC_INIT;
134    int dbd_cmd = dbd_rebuild;
135    int cdir = -1;
136    AFPObj obj = { 0 };
137    struct vol *vol = NULL;
138    const char *volpath = NULL;
139
140    int c;
141    while ((c = getopt(argc, argv, ":cfF:rstvV")) != -1) {
142        switch(c) {
143        case 'c':
144            flags |= DBD_FLAGS_V2TOEA;
145            break;
146        case 'f':
147            flags |= DBD_FLAGS_FORCE;
148            break;
149        case 'F':
150            obj.cmdlineconfigfile = strdup(optarg);
151            break;
152        case 'r':
153            /* the default */
154            break;
155        case 's':
156            dbd_cmd = dbd_scan;
157            flags |= DBD_FLAGS_SCAN;
158            break;
159        case 't':
160            flags |= DBD_FLAGS_STATS;
161            break;
162        case 'v':
163            flags |= DBD_FLAGS_VERBOSE;
164            break;
165        case 'V':
166            printf("dbd %s\n", VERSION);
167            exit(0);
168        case ':':
169        case '?':
170            usage();
171            exit(EXIT_FAILURE);
172            break;
173        }
174    }
175
176    if ( (optind + 1) != argc ) {
177        usage();
178        exit(EXIT_FAILURE);
179    }
180    volpath = argv[optind];
181
182    if (geteuid() != 0) {
183        usage();
184        exit(EXIT_FAILURE);
185    }
186    /* Inhereting perms in ad_mkdir etc requires this */
187    ad_setfuid(0);
188
189    setvbuf(stdout, (char *) NULL, _IONBF, 0);
190
191    /* Remember cwd */
192    if ((cdir = open(".", O_RDONLY)) < 0) {
193        dbd_log( LOGSTD, "Can't open dir: %s", strerror(errno));
194        exit(EXIT_FAILURE);
195    }
196
197    /* Setup signal handling */
198    set_signal();
199
200    /* Load config */
201    if (afp_config_parse(&obj, "dbd") != 0) {
202        dbd_log( LOGSTD, "Couldn't load afp.conf");
203        exit(EXIT_FAILURE);
204    }
205
206    /* Initialize CNID subsystem */
207    cnid_init();
208
209    /* Setup logging. Should be portable among *NIXes */
210    if (flags & DBD_FLAGS_VERBOSE)
211        setuplog("default:note, cnid:debug", "/dev/tty");
212    else
213        setuplog("default:note", "/dev/tty");
214
215    if (load_volumes(&obj) != 0) {
216        dbd_log( LOGSTD, "Couldn't load volumes");
217        exit(EXIT_FAILURE);
218    }
219
220    if ((vol = getvolbypath(&obj, volpath)) == NULL) {
221        dbd_log( LOGSTD, "Couldn't find volume for '%s'", volpath);
222        exit(EXIT_FAILURE);
223    }
224
225    if (load_charset(vol) != 0) {
226        dbd_log( LOGSTD, "Couldn't load charsets for '%s'", volpath);
227        exit(EXIT_FAILURE);
228    }
229
230    /* open volume */
231    if (STRCMP(vol->v_cnidscheme, != , "dbd")) {
232        dbd_log(LOGSTD, "\"%s\" isn't a \"dbd\" CNID volume", vol->v_path);
233        exit(EXIT_FAILURE);
234    }
235    if ((vol->v_cdb = cnid_open(vol->v_path,
236                                0000,
237                                "dbd",
238                                vol->v_flags & AFPVOL_NODEV ? CNID_FLAG_NODEV : 0,
239                                vol->v_cnidserver,
240                                vol->v_cnidport)) == NULL) {
241        dbd_log(LOGSTD, "Cant initialize CNID database connection for %s", vol->v_path);
242        exit(EXIT_FAILURE);
243    }
244
245    if (vol->v_adouble == AD_VERSION_EA)
246        dbd_log( LOGDEBUG, "adouble:ea volume");
247    else if (vol->v_adouble == AD_VERSION2)
248        dbd_log( LOGDEBUG, "adouble:v2 volume");
249    else {
250        dbd_log( LOGSTD, "unknown adouble volume");
251        exit(EXIT_FAILURE);
252    }
253
254    /* -C v2 to ea conversion only on adouble:ea volumes */
255    if ((flags & DBD_FLAGS_V2TOEA) && (vol->v_adouble!= AD_VERSION_EA)) {
256        dbd_log( LOGSTD, "Can't run adouble:v2 to adouble:ea conversion because not an adouble:ea volume");
257        exit(EXIT_FAILURE);
258    }
259
260    /* Sanity checks to ensure we can touch this volume */
261    if (vol->v_vfs_ea != AFPVOL_EA_AD && vol->v_vfs_ea != AFPVOL_EA_SYS) {
262        dbd_log( LOGSTD, "Unknown Extended Attributes option: %u", vol->v_vfs_ea);
263        exit(EXIT_FAILURE);
264    }
265
266    if (flags & DBD_FLAGS_FORCE) {
267        if (cnid_wipe(vol->v_cdb) != 0) {
268            dbd_log( LOGSTD, "Failed to wipe CNID db");
269            EC_FAIL;
270        }
271    }
272
273    /* Now execute given command scan|rebuild|dump */
274    switch (dbd_cmd) {
275    case dbd_scan:
276    case dbd_rebuild:
277        if (cmd_dbd_scanvol(vol, flags) < 0) {
278            dbd_log( LOGSTD, "Error repairing database.");
279        }
280        break;
281    }
282
283EC_CLEANUP:
284    if (vol)
285        cnid_close(vol->v_cdb);
286
287    if (cdir != -1 && (fchdir(cdir) < 0))
288        dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
289
290    if (ret == 0)
291        exit(EXIT_SUCCESS);
292    else
293        exit(EXIT_FAILURE);
294}
295