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