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