/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * main.c * * entry point for debugfs.ocfs2 * * Copyright (C) 2004, 2007 Oracle. All rights reserved. * * 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 (at your option) 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., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. * * Authors: Sunil Mushran, Manish Singh */ #include "main.h" #include #include #define PROMPT "debugfs: " extern dbgfs_gbls gbls; static int decodemode = 0; static int encodemode = 0; static int arg_ind = 0; static int logmode = 0; struct log_entry { char *mask; char *action; }; static GList *loglist = NULL; static void usage (char *progname) { g_print ("usage: %s -l [ ... [allow|off|deny]] ...\n", progname); g_print ("usage: %s -d, --decode \n", progname); g_print ("usage: %s -e, --encode \n", progname); g_print ("usage: %s [-f cmdfile] [-R request] [-i] [-s backup#] [-V] [-w] [-n] [-?] [device]\n", progname); g_print ("\t-f, --file \t\tExecute commands in cmdfile\n"); g_print ("\t-R, --request \t\tExecute a single command\n"); g_print ("\t-s, --superblock \tOpen the device using a backup superblock\n"); g_print ("\t-i, --image\t\t\tOpen an o2image file\n"); g_print ("\t-w, --write\t\t\tOpen in read-write mode instead of the default of read-only\n"); g_print ("\t-V, --version\t\t\tShow version\n"); g_print ("\t-n, --noprompt\t\t\tHide prompt\n"); g_print ("\t-?, --help\t\t\tShow this help\n"); exit (0); } static void print_version (char *progname) { fprintf(stderr, "%s %s\n", progname, VERSION); } static void process_one_list(GList *list, char *action) { GList *tmp; struct log_entry *entry = NULL; while (list) { tmp = loglist; while (tmp) { entry = tmp->data; if (!strcasecmp(entry->mask, list->data)) break; tmp = tmp->next; } if (tmp) { entry->action = action; } else { entry = g_new(struct log_entry, 1); entry->action = action; entry->mask = list->data; loglist = g_list_append(loglist, entry); } list = list->next; } } static void fill_log_list(int argc, char **argv, int startind) { int i; GList *tmplist = NULL; for (i = startind; i < argc; i++) { if (!strcasecmp(argv[i], "allow") || !strcasecmp(argv[i], "deny") || !strcasecmp(argv[i], "off")) { process_one_list(tmplist, argv[i]); g_list_free(tmplist); tmplist = NULL; } else { tmplist = g_list_append(tmplist, argv[i]); } } } static void process_decode_lockres(int argc, char **argv, int startind) { int i; errcode_t ret; enum ocfs2_lock_type type; uint64_t blkno = 0; uint32_t generation = 0; uint64_t parent = 0; if (startind + 1 > argc) { usage(gbls.progname); exit(1); } for (i = startind; i < argc; ++i) { ret = ocfs2_decode_lockres(argv[i], &type, &blkno, &generation, &parent); if (ret) continue; printf("Lockres: %s\n", argv[i]); printf("Type: %s\n", ocfs2_lock_type_string(type)); if (blkno) printf("Block: %"PRIu64"\n", blkno); if (generation) printf("Generation: 0x%08x\n", generation); if (parent) printf("Parent: %"PRIu64"\n", parent); printf("\n"); } return ; } static void process_encode_lockres(int argc, char **argv, int startind) { int i; errcode_t ret; enum ocfs2_lock_type type; uint64_t blkno; uint64_t extra; /* generation or parent */ char lock[OCFS2_LOCK_ID_MAX_LEN]; char tmp[OCFS2_LOCK_ID_MAX_LEN]; if (startind + 3 > argc) { usage(gbls.progname); exit(1); } i = startind; type = ocfs2_get_lock_type(argv[i++][0]); blkno = strtoull(argv[i++], NULL, 0); extra = strtoull(argv[i++], NULL, 0); if (type == OCFS2_LOCK_TYPE_DENTRY) { ret = ocfs2_encode_lockres(type, blkno, 0, extra, tmp); if (!ret) ret = ocfs2_printable_lockres(tmp, lock, sizeof(lock)); } else ret = ocfs2_encode_lockres(type, blkno, (uint32_t)extra, 0, lock); if (ret) { com_err(gbls.progname, ret, "while encoding lockname"); return ; } printf("%s\n", lock); return ; } static void get_options(int argc, char **argv, dbgfs_opts *opts) { int c; char *ptr = NULL; static struct option long_options[] = { { "file", 1, 0, 'f' }, { "request", 1, 0, 'R' }, { "version", 0, 0, 'V' }, { "help", 0, 0, '?' }, { "write", 0, 0, '?' }, { "log", 0, 0, 'l' }, { "noprompt", 0, 0, 'n' }, { "decode", 0, 0, 'd' }, { "encode", 0, 0, 'e' }, { "superblock", 0, 0, 's' }, { "image", 0, 0, 'i' }, { 0, 0, 0, 0} }; while (1) { if (decodemode || encodemode || logmode) break; c = getopt_long(argc, argv, "lf:R:deV?wns:i", long_options, NULL); if (c == -1) break; switch (c) { case 'f': opts->cmd_file = strdup(optarg); if (!strlen(opts->cmd_file)) { usage(gbls.progname); exit(1); } break; case 'R': opts->one_cmd = strdup(optarg); if (!strlen(opts->one_cmd)) { usage(gbls.progname); exit(1); } break; case 'd': decodemode++; break; case 'e': encodemode++; break; case 'i': opts->imagefile = 1; break; case 'l': logmode++; break; case 'w': opts->allow_write = 1; break; case 'n': opts->no_prompt = 1; break; case '?': print_version(gbls.progname); usage(gbls.progname); exit(0); break; case 'V': print_version(gbls.progname); exit(0); break; case 's': opts->sb_num = strtoul(optarg, &ptr, 0); break; default: usage(gbls.progname); break; } } if (optind < argc) { if (logmode) fill_log_list(argc, argv, optind); else opts->device = strdup(argv[optind]); } if (decodemode || encodemode) arg_ind = optind; return ; } static char * get_line (FILE *stream, int no_prompt) { char *line; static char buf[1024]; int i; if (stream) { while (1) { if (!fgets(buf, sizeof(buf), stream)) return NULL; line = buf; i = strlen(line); if (i) buf[i - 1] = '\0'; g_strchug(line); if (strlen(line)) break; } } else { if (no_prompt) line = readline(NULL); else line = readline(PROMPT); if (line && *line) { g_strchug(line); add_history (line); } } return line; } #define LOG_CTL_PROC "/proc/fs/ocfs2_nodemanager/log_mask" static int set_logmode_proc(struct log_entry *entry) { FILE *f; f = fopen(LOG_CTL_PROC, "w"); if (!f) { fprintf(stderr, "%s: Unable to open \"%s\": %s\n", gbls.progname, LOG_CTL_PROC, strerror(errno)); return 1; } fprintf(f, "%s %s\n", entry->mask, entry->action); fclose(f); return 0; } #define LOG_CTL_SYSFS_DIR_OLD "/sys/o2cb/logmask" #define LOG_CTL_SYSFS_DIR "/sys/fs/o2cb/logmask" #define LOG_CTL_SYSFS_FORMAT "%s/%s" static int set_logmode_sysfs(const char *path, struct log_entry *entry) { FILE *f; char *logpath; logpath = g_strdup_printf(LOG_CTL_SYSFS_FORMAT, path, entry->mask); f = fopen(logpath, "w"); g_free(logpath); if (!f) { fprintf(stderr, "%s: Unable to write log mask \"%s\": %s\n", gbls.progname, entry->mask, strerror(errno)); return 1; } fprintf(f, "%s\n", entry->action); fclose(f); return 0; } static int get_logmode_sysfs(const char *path, const char *name) { char *logpath; char *current_mask; logpath = g_strdup_printf(LOG_CTL_SYSFS_FORMAT, path, name); if (g_file_get_contents(logpath, ¤t_mask, NULL, NULL)) { fprintf(stdout, "%s %s", name, current_mask); } g_free(logpath); return 0; } static void run_logmode_proc(void) { GList *tmp; char *current_mask; if (loglist) { tmp = loglist; while (tmp) { if (set_logmode_proc(tmp->data)) break; tmp = tmp->next; } } else { if (g_file_get_contents(LOG_CTL_PROC, ¤t_mask, NULL, NULL)) { fprintf(stdout, "%s", current_mask); } } } static void run_logmode_sysfs(const char *path) { GList *tmp; DIR *dir; struct dirent *d; if (loglist) { tmp = loglist; while (tmp) { if (set_logmode_sysfs(path, tmp->data)) break; tmp = tmp->next; } } else { dir = opendir(path); if (dir) { while ((d = readdir(dir)) != NULL) get_logmode_sysfs(path, d->d_name); closedir(dir); } } } static void run_logmode(void) { struct stat stat_buf; if (!stat(LOG_CTL_SYSFS_DIR, &stat_buf) && S_ISDIR(stat_buf.st_mode)) run_logmode_sysfs(LOG_CTL_SYSFS_DIR); else if (!stat(LOG_CTL_SYSFS_DIR_OLD, &stat_buf) && S_ISDIR(stat_buf.st_mode)) run_logmode_sysfs(LOG_CTL_SYSFS_DIR_OLD); else if (!stat(LOG_CTL_PROC, &stat_buf) && S_ISREG(stat_buf.st_mode)) run_logmode_proc(); } int main (int argc, char **argv) { char *line; dbgfs_opts opts; FILE *cmd = NULL; initialize_o2cb_error_table(); initialize_ocfs_error_table(); #define INSTALL_SIGNAL(sig) \ do { \ if (signal(sig, handle_signal) == SIG_ERR) { \ printf("Could not set " #sig "\n"); \ goto bail; \ } \ } while (0) INSTALL_SIGNAL(SIGTERM); INSTALL_SIGNAL(SIGINT); setbuf(stdout, NULL); setbuf(stderr, NULL); memset(&opts, 0, sizeof(opts)); memset(&gbls, 0, sizeof(gbls)); gbls.progname = basename(argv[0]); get_options(argc, argv, &opts); if (logmode) { run_logmode(); goto bail; } if (decodemode) { process_decode_lockres(argc, argv, arg_ind); goto bail; } if (encodemode) { process_encode_lockres(argc, argv, arg_ind); goto bail; } gbls.allow_write = opts.allow_write; gbls.imagefile = opts.imagefile; if (!opts.cmd_file) gbls.interactive++; if (opts.device) { if (opts.sb_num) line = g_strdup_printf ("open %s -s %u", opts.device, opts.sb_num); else line = g_strdup_printf ("open %s", opts.device); do_command (line); g_free (line); } if (opts.one_cmd) { do_command (opts.one_cmd); goto bail; } if (opts.cmd_file) { cmd = fopen(opts.cmd_file, "r"); if (!cmd) { com_err(argv[0], errno, "'%s'", opts.cmd_file); goto bail; } } if (!opts.no_prompt) print_version (gbls.progname); while (1) { line = get_line(cmd, opts.no_prompt); if (line) { if (!gbls.interactive && !opts.no_prompt) fprintf (stdout, "%s%s\n", PROMPT, line); do_command (line); if (gbls.interactive) free (line); } else { printf ("\n"); raise (SIGTERM); exit (0); } } bail: if (cmd) fclose(cmd); if (opts.cmd_file) free(opts.cmd_file); if (opts.device) free(opts.device); return 0; }