/****************************-*-C-*-*********************************** * $Id: bksh.c,v 1.50 2004/03/05 02:40:18 anarcat Exp $ ********************************************************************** * Backup wrapper shell for ssh ********************************************************************** * Copyright (C) 2001-2003 The Anarcat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See also http://www.fsf.org *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) && __FreeBSD__ >= 2 #include # if __FreeBSD_version >= 430000 #include # else #include "basename.h" # endif #else /* compatibility with FreeBSD extensions */ # if defined(__NetBSD__) int optreset = 0; #include "basename.h" # else char optreset = 0; #include "basename.h" # endif #endif #include "bksh.h" /* recursively create missing directories */ int mkdirp(const char* path, mode_t mode) { int ret; char* wpath; /* strip any trailing slash, since POSIX mkdir() doesn't accept it */ ret = strlen(path); if (path[ret] == '/') { wpath = strdup(path); wpath[ret] = '\0'; path = wpath; } if ( (ret = mkdir(path, mode)) < 0) { if (errno == ENOENT) { wpath = dirname(path); if (strcmp(wpath, path) == 0) return -1; fprintf(stderr, "directory %s missing, trying to create %s\n", path, wpath); wpath = strdup(wpath); /* dirname returns static storage */ mkdirp(wpath, mode); free(wpath); return mkdir(path, mode); } else return -1; } else return ret; } int main (int argc, char** argv) { char buffer[BUFSIZ], datadir[MAXPATHLEN], filename[MAXPATHLEN]; char *n; /* temporary handle for arg parsing */ int i, fd, ch; int max_baks = MAX_BAKS; /* command line parsing */ while ((ch = getopt(argc, argv, "chV?" )) != -1) { switch (ch) { case 'c': /* we ignore the -c arg */ break; case 'V': printf("bksh $Name: BKSH_REL_1_7 $\n"); printf("Compiled with:\n"); printf("Static %d backups maximum\n", max_baks); #ifdef FILE_BACKUP printf("Regular to-file backups\n"); #else printf("Tape-only backups\n"); #endif printf("Default backup dir: %s, default filename: %s\n", DEF_DATADIR, DEF_FILENAME); printf("Copyright (c) 2002-2003 The Anarcat \n"); exit(1); break; default: fprintf(stderr, "wrong argument: %c\n", ch); break; } } argc -= optind; argv += optind; /* manually parse the remaining options * * we tokenize each remaining argument into a args array that will * be acceptable input to getopt(3) */ if (argc >= 1) { char *args[MAX_ARGS], *arg; int i, argcount = 0; for (i = 0; i < argc; i++) { /* tokenize the string */ for (arg = argv[i]; *arg; arg++) { /* eat whitespace */ while (*arg && isspace(*arg)) arg++; if (!*arg) break; if (argcount < MAX_ARGS) { args[argcount++] = arg; /* eat non whitespace */ while (*arg && !isspace(*arg)) arg++; if (!*arg) break; *arg = 0; /* terminate string here */ } else { fprintf(stderr, "maximum argument count (%d) exceeded\n", MAX_ARGS); break; } } } optind = 1; optreset = 1; /* at this point we should have an array of args properly constructed */ while ((ch = getopt(argcount, args, "t:" )) != -1) { if (ch == 't') { char* m; max_baks = strtol(optarg, &m, 0); if (*m) { /* oups, did not stop at the end */ fprintf(stderr, "invalid numeric value: %s, ignoring\n", optarg); max_baks = MAX_BAKS; } } else { fprintf(stderr, "wrong argument: %c\n", ch); } } } #ifdef FILE_BACKUP if ( (argc >= 1) && ((strcmp(argv[0], "/dev/sa0") == 0) || (strcmp(argv[0], "/dev/nsa0") == 0) || (strcmp(argv[0], "/dev/esa0") == 0)) ) { strcpy(filename, argv[0]); } else { char backup_file[MAXPATHLEN]; char *back_name = DEF_FILENAME; char *home = getenv("HOME"); char *client_hn = getenv("SSH_CLIENT"); /* mandatory environment */ if (home == NULL) { fprintf(stderr, "HOME not set, aborting\n"); exit(1); } if (client_hn == NULL) { fprintf(stderr, "SSH_CLIENT not set, aborting\n"); exit(1); } /* first space in the SSH_CLIENT string delimits hostname */ for (n = client_hn; *n && *n != ' '; n++) { } *n = '\0'; /* end the string after the hostname */ snprintf(datadir, MAXPATHLEN, "%s/%s/%s", home, DEF_DATADIR, client_hn); /* make hierarchy leading to the drop dir */ mkdirp(datadir, 0700); /* we take the backup name from the first argument */ if (argc >= 1) back_name = basename(argv[0]); snprintf(filename, MAXPATHLEN, "%s/%s", datadir, back_name); if (max_baks > 1) { /* rotate the files */ /* 1- find last archive */ for (i = 1; i < (max_baks-1) && (access(filename, F_OK) == 0); i++) { snprintf(filename, MAXPATHLEN, "%s/%s.%d", datadir, back_name, i); } if (i < (max_baks-1)) i--; /* filename i didn't exist */ /* 2- rotate numerically named files */ while (i > 1) { snprintf(backup_file, MAXPATHLEN, "%s/%s.%d", datadir, back_name, i); snprintf(filename, MAXPATHLEN, "%s/%s.%d", datadir, back_name, (--i)); rename(filename, backup_file); } /* 3- rotate last file left */ if (i == 1) { snprintf(backup_file, MAXPATHLEN, "%s/%s.%d", datadir, back_name, i); snprintf(filename, MAXPATHLEN, "%s/%s", datadir, back_name); rename(filename, backup_file); } } else { /* only one backup file allowed, remove prior art */ unlink(filename); } } #else strcpy(filename, "/dev/nsa0"); #endif /* write the file */ fd = open(filename, O_WRONLY | O_CREAT, 0400); if (fd < 0) { fprintf(stderr, "can't open file %s: %s\n", filename, strerror(errno)); exit(1); } while ( (i = read(STDIN_FILENO, buffer, BUFSIZ)) > 0) { if (write(fd, buffer, i) < 0) { fprintf(stderr, "can't write to file %s: %s", filename, strerror(errno)); exit(1); } } close (fd); printf("written to %s\n", filename); if (i < 0) { fprintf(stderr, "cant't read from stdin: %s", strerror(errno)); exit(1); } return 0; }