]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
wdctl: rewrite to use lib/tt
authorKarel Zak <kzak@redhat.com>
Thu, 3 May 2012 15:29:31 +0000 (17:29 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 3 May 2012 15:29:31 +0000 (17:29 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/Makefile.am
sys-utils/wdctl.c

index 132ce5dbac13bf204336a3192db83e7550c14751..a1a914b9eb6fbfc33788a5412c3c2780c360d7f9 100644 (file)
@@ -26,7 +26,7 @@ if LINUX
 # Linux-only utils with no another dependencies. All another dependencies have
 # to be resolved in configure.ac end exported to makefiles by BUILD_*.
 #
-bin_PROGRAMS += dmesg wdctl
+bin_PROGRAMS += dmesg
 sbin_PROGRAMS += ctrlaltdel fsfreeze fstrim
 usrbin_exec_PROGRAMS += cytune setarch
 usrsbin_exec_PROGRAMS += ldattach tunelp rtcwake
@@ -184,7 +184,13 @@ ipcmk_SOURCES = ipcmk.c $(top_srcdir)/lib/strutils.c
 ipcrm_SOURCES = ipcrm.c  $(top_srcdir)/lib/strutils.c
 flock_SOURCES = flock.c $(top_srcdir)/lib/strutils.c
 ldattach_SOURCES = ldattach.c $(top_srcdir)/lib/strutils.c
-wdctl_SOURCES = wdctl.c
+
+bin_PROGRAMS += wdctl
+wdctl_SOURCES = \
+       wdctl.c \
+       $(top_srcdir)/lib/strutils.c \
+       $(top_srcdir)/lib/tt.c \
+       $(top_srcdir)/lib/mbsalign.c
 
 if BUILD_MOUNTPOINT
 bin_PROGRAMS += mountpoint
index 4c6f87442fb7f3fe7314488731e9af579cd48417..1192348f3a45b8bb1b8ca773ff1360d67d358725 100644 (file)
@@ -4,6 +4,7 @@
  * wdctl(8) - show hardware watchdog status
  *
  * Copyright (C) 2012 Lennart Poettering
+ * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
  *
  * 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
 #include <getopt.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <signal.h>
+#include <assert.h>
 
 #include "nls.h"
 #include "c.h"
 #include "closestream.h"
 #include "pathnames.h"
+#include "strutils.h"
+#include "tt.h"
 
-static const struct {
-       uint32_t flag;
-       const char *name;
-} flag_names[] = {
-       { WDIOF_OVERHEAT,      N_("OVERHEAT: Reset due to CPU overheat") },
-       { WDIOF_FANFAULT,      N_("FANFAULT: Fan failed") },
-       { WDIOF_EXTERN1,       N_("EXTERN1: External relay 1") },
-       { WDIOF_EXTERN2,       N_("EXTERN2: External relay 2") },
-       { WDIOF_POWERUNDER,    N_("POWERUNDER: Power bad/power fault") },
-       { WDIOF_CARDRESET,     N_("CARDRESET: Card previously reset the CPU") },
-       { WDIOF_POWEROVER,     N_("POWEROVER: Power over voltage") },
-       { WDIOF_SETTIMEOUT,    N_("SETTIMEOUT: Set timeout (in seconds)") },
-       { WDIOF_MAGICCLOSE,    N_("MAGICCLOSE: Supports magic close char") },
-       { WDIOF_PRETIMEOUT,    N_("PRETIMEOUT: Pretimeout (in seconds)") },
-       { WDIOF_KEEPALIVEPING, N_("KEEPALIVEPING: Keep alive ping reply") }
+struct wdflag {
+       uint32_t        flag;
+       const char      *name;
+       const char      *description;
 };
 
-static void usage(int status)
+static const struct wdflag wdflags[] = {
+       { WDIOF_CARDRESET,     "CARDRESET",  N_("Card previously reset the CPU") },
+       { WDIOF_EXTERN1,       "EXTERN1",    N_("External relay 1") },
+       { WDIOF_EXTERN2,       "EXTERN2",    N_("External relay 2") },
+       { WDIOF_FANFAULT,      "FANFAULT",   N_("Fan failed") },
+       { WDIOF_KEEPALIVEPING, "KEEPALIVEPING", N_("Keep alive ping reply") },
+       { WDIOF_MAGICCLOSE,    "MAGICCLOSE", N_("Supports magic close char") },
+       { WDIOF_OVERHEAT,      "OVERHEAT",   N_("Reset due to CPU overheat") },
+       { WDIOF_POWEROVER,     "POWEROVER",  N_("Power over voltage") },
+       { WDIOF_POWERUNDER,    "POWERUNDER", N_("Power bad/power fault") },
+       { WDIOF_PRETIMEOUT,    "PRETIMEOUT", N_("Pretimeout (in seconds)") },
+       { WDIOF_SETTIMEOUT,    "SETTIMEOUT", N_("Set timeout (in seconds)") }
+};
+
+
+/* column names */
+struct colinfo {
+       const char *name; /* header */
+       double     whint; /* width hint (N < 1 is in percent of termwidth) */
+       int        flags; /* TT_FL_* */
+       const char *help;
+};
+
+enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS };
+
+/* columns descriptions */
+static struct colinfo infos[] = {
+       [COL_FLAG]    = { "FLAG",        14,  0, N_("flag name") },
+       [COL_DESC]    = { "DESCRIPTION", 0.1, TT_FL_TRUNC, N_("flag description") },
+       [COL_STATUS]  = { "STATUS",      1,   TT_FL_RIGHT, N_("flag status") },
+       [COL_BSTATUS] = { "BOOT-STATUS", 1,   TT_FL_RIGHT, N_("flag boot status") }
+};
+
+#define NCOLS ARRAY_SIZE(infos)
+static int columns[NCOLS], ncolumns;
+
+struct wdinfo {
+       char            *device;
+
+       int             timeout;
+       int             timeleft;
+       int             pretimeout;
+
+       uint32_t        status;
+       uint32_t        bstatus;
+
+       struct watchdog_info ident;
+
+       unsigned int    has_timeout : 1,
+                       has_timeleft : 1,
+                       has_pretimeout : 1;
+};
+
+/* converts flag name to flag bit */
+static long name2bit(const char *name, size_t namesz)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
+               const char *cn = wdflags[i].name;
+               if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+                       return wdflags[i].flag;
+       }
+       warnx(_("unknown flag: %s"), name);
+       return -1;
+}
+
+static int column2id(const char *name, size_t namesz)
+{
+       size_t i;
+
+       for (i = 0; i < NCOLS; i++) {
+               const char *cn = infos[i].name;
+               if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+                       return i;
+       }
+       warnx(_("unknown column: %s"), name);
+       return -1;
+}
+
+static int get_column_id(int num)
+{
+       assert(ARRAY_SIZE(columns) == NCOLS);
+       assert(num < ncolumns);
+       assert(columns[num] < (int) NCOLS);
+
+       return columns[num];
+}
+
+static struct colinfo *get_column_info(unsigned num)
+{
+       return &infos[ get_column_id(num) ];
+}
+
+static void usage(FILE *out)
 {
-       FILE *out = status == EXIT_SUCCESS ? stdout : stderr;
+       size_t i;
 
        fputs(USAGE_HEADER, out);
        fprintf(out,
@@ -60,125 +148,311 @@ static void usage(int status)
        fprintf(out,
              _(" -d, --device <path>   device to use (default is %s)\n"), _PATH_WATCHDOG_DEV);
 
+       fputs(_(" -f, --flags <list>    print selected flags only\n"
+               " -F, --noflags         don't print information about flags\n"
+               " -n, --noheadings      don't print headings\n"
+               " -I, --noident         don't print watchdog identity information\n"
+               " -T, --notimeouts      don't print watchdog timeouts\n"
+               " -o, --output <list>   output columns of the flags\n"
+               " -P, --pairs           use key=\"value\" output format\n"
+               " -r, --raw             use raw output format\n"), out);
+
        fputs(USAGE_SEPARATOR, out);
        fputs(USAGE_HELP, out);
        fputs(USAGE_VERSION, out);
+       fputs(USAGE_SEPARATOR, out);
+
+       fprintf(out, _("\nAvailable columns:\n"));
+       for (i = 0; i < ARRAY_SIZE(infos); i++)
+               fprintf(out, " %13s  %s\n", infos[i].name, _(infos[i].help));
+
        fprintf(out, USAGE_MAN_TAIL("wdctl(1)"));
 
-       exit(status);
+       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
-static void print_options(uint32_t options)
+static void add_flag_line(struct tt *tt, struct wdinfo *wd, const struct wdflag *fl)
 {
-       unsigned i;
+       int i;
+       struct tt_line *line;
 
-       if (options == 0) {
-               puts(_("\tNo flags set."));
+       line = tt_add_line(tt, NULL);
+       if (!line) {
+               warn(_("failed to add line to output"));
                return;
        }
 
-       for (i = 0; i < ARRAY_SIZE(flag_names); i++) {
-               if (options & flag_names[i].flag)
-                       printf("\t%s\n", _(flag_names[i].name));
-               options &= ~flag_names[i].flag;
-       }
+       for (i = 0; i < ncolumns; i++) {
+               const char *str = NULL;
+
+               switch (get_column_id(i)) {
+               case COL_FLAG:
+                       str = fl->name;
+                       break;
+               case COL_DESC:
+                       str = fl->description;
+                       break;
+               case COL_STATUS:
+                       str = wd->status & fl->flag ? "1" : "0";
+                       break;
+               case COL_BSTATUS:
+                       str = wd->bstatus & fl->flag ? "1" : "0";
+                       break;
+               default:
+                       break;
+               }
 
-       if (options)
-               printf(_("\tUnknown flags 0x%x\n"), options);
+               if (str)
+                       tt_line_set_data(line, i, str);
+       }
 }
 
-int main(int argc, char *argv[])
+static int show_flags(struct wdinfo *wd, int tt_flags, uint32_t wanted)
 {
-       static const struct option longopts[] = {
-               { "help",    no_argument,       0, 'h' },
-               { "version", no_argument,       0, 'V'},
-               { "device",  required_argument, 0, 'd' },
-               { NULL, 0, 0, 0 }
-       };
-
-       int c, status, sec, fd;
-       const char *device = _PATH_WATCHDOG_DEV;
-       struct watchdog_info ident;
-
-       setlocale(LC_MESSAGES, "");
-       bindtextdomain(PACKAGE, LOCALEDIR);
-       textdomain(PACKAGE);
-       atexit(close_stdout);
+       size_t i;
+       int rc = -1;
+       struct tt *tt;
+       uint32_t flags;
+
+       /* create output table */
+       tt = tt_new_table(tt_flags);
+       if (!tt) {
+               warn(_("failed to initialize output table"));
+               return -1;
+       }
 
-       while((c = getopt_long(argc, argv, "hVd:", longopts, NULL)) != -1) {
+       /* define columns */
+       for (i = 0; i < (size_t) ncolumns; i++) {
+               struct colinfo *col = get_column_info(i);
 
-               switch(c) {
-               case 'h':
-                       usage(EXIT_SUCCESS);
-               case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
-               case 'd':
-                       device = optarg;
-                       break;
-               default:
-                       usage(EXIT_FAILURE);
+               if (!tt_define_column(tt, col->name, col->whint, col->flags)) {
+                       warnx(_("failed to initialize output column"));
+                       goto done;
                }
        }
 
-       if (optind < argc)
-               usage(EXIT_FAILURE);
+       /* fill-in table with data
+        * -- one line for each supported flag (option)  */
+       flags = wd->ident.options;
 
-       fd = open(device, O_WRONLY|O_CLOEXEC);
-       if (fd < 0) {
-               if (errno == EBUSY)
-                       errx(EXIT_FAILURE, _("Watchdog already in use, terminating."));
+       for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
+               if (wanted && !(wanted & wdflags[i].flag))
+                       ; /* ignore */
+               else if (flags & wdflags[i].flag)
+                       add_flag_line(tt, wd, &wdflags[i]);
 
-               err(EXIT_FAILURE, _("Failed to open watchdog device"));
+               flags &= ~wdflags[i].flag;
        }
 
-        if (ioctl(fd, WDIOC_GETSUPPORT, &ident) >= 0) {
-                printf(_("Identity:\n\t%s\n"
-                        "Firmware version:\n\t%x\n"
-                        "Supported options:\n"),
-                        ident.identity,
-                        ident.firmware_version);
-               print_options(ident.options);
-       }
+       if (flags)
+               warnx(_("%s: unknown flags 0x%x\n"), wd->device, flags);
 
-       if (ioctl(fd, WDIOC_GETSTATUS, &status) >= 0) {
-               puts(_("Status:"));
-               print_options((uint32_t) status);
-       }
+       tt_print_table(tt);
+       rc = 0;
+done:
+       tt_free_table(tt);
+       return rc;
+}
 
-       if (ioctl(fd, WDIOC_GETBOOTSTATUS, &status) >= 0) {
-               puts(_("Boot status:"));
-               print_options((uint32_t) status);
-       }
+/*
+ * Warning: successfully opened watchdog has to be properly closed with magic
+ * close character otherwise the machine will be rebooted!
+ *
+ * Don't use err() or exit() here!
+ */
+static int read_watchdog(struct wdinfo *wd)
+{
+       int fd;
+       sigset_t sigs, oldsigs;
+
+       assert(wd->device);
+
+       sigemptyset(&oldsigs);
+       sigfillset(&sigs);
+       sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
 
-       if (ioctl(fd, WDIOC_GETTIMEOUT, &sec) >= 0)
-               printf(_("Timeout:\n\t%is\n"), sec);
+       fd = open(wd->device, O_WRONLY|O_CLOEXEC);
 
-       if (ioctl(fd, WDIOC_GETPRETIMEOUT, &sec) >= 0)
-               printf(_("Pre-Timeout:\n\t%is\n"), sec);
+       if (fd < 0) {
+               if (errno == EBUSY)
+                       errx(EXIT_FAILURE, _("%s: watchdog already in use, terminating."),
+                                       wd->device);
+               err(EXIT_FAILURE, _("%s: failed to open watchdog device"),
+                               wd->device);
+       }
 
-       if (ioctl(fd, WDIOC_GETTIMELEFT, &sec) >= 0)
-               printf(_("Time Left:\n\t%is\n"), sec);
+       if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
+               warn(_("%s: failed to get information about watchdog"), wd->device);
+       else {
+               ioctl(fd, WDIOC_GETSTATUS, &wd->status);
+               ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
+
+               if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
+                       wd->has_timeout = 1;
+               if (ioctl(fd, WDIOC_GETPRETIMEOUT, &wd->pretimeout) >= 0)
+                       wd->has_pretimeout = 1;
+               if (ioctl(fd, WDIOC_GETTIMELEFT, &wd->timeleft) >= 0)
+                       wd->has_timeleft = 1;
+       }
 
        for (;;) {
                /* We just opened this to query the state, not to arm
                 * it hence use the magic close character */
-
                static const char v = 'V';
 
                if (write(fd, &v, 1) >= 0)
                        break;
-
                if (errno != EINTR) {
-                       warn(_("Failed to disarm watchdog"));
+                       warn(_("%s: failed to disarm watchdog"), wd->device);
                        break;
                }
-
                /* Let's try hard, since if we don't get this right
                 * the machine might end up rebooting. */
        }
 
        close(fd);
+       sigprocmask(SIG_SETMASK, &oldsigs, NULL);
+
+       return 0;
+}
+
+static void show_timeouts(struct wdinfo *wd)
+{
+       const char *sep;
+
+       if (!wd->has_timeout && !wd->has_pretimeout && !wd->has_timeleft)
+               return;
+
+       if (wd->has_timeout) {
+               printf(_("Timeout"));
+               sep = "/";
+       }
+       if (wd->has_pretimeout) {
+               printf(_("%sPre-timeout"), sep);
+               sep = "/";
+       }
+       if (wd->has_timeleft)
+               printf(_("%sTimeleft"), sep);
+
+       fputs(": ", stdout);
+       sep = NULL;
+       if (wd->has_timeout) {
+               printf("%i", wd->timeout);
+               sep = "/";
+       }
+       if (wd->has_pretimeout) {
+               printf("%s%i", sep, wd->pretimeout);
+               sep = "/";
+       }
+       if (wd->has_timeleft)
+               printf("%s%i", sep, wd->timeleft);
+
+       fputs(_(" seconds\n"), stdout);
+}
 
-       return EXIT_SUCCESS;
+int main(int argc, char *argv[])
+{
+       struct wdinfo wd = { .device = _PATH_WATCHDOG_DEV };
+
+       int c, tt_flags = 0, rc = 0;
+       char noflags = 0, noident = 0, notimeouts = 0;
+       uint32_t wanted = 0;
+
+       static const struct option long_opts[] = {
+               { "device",     required_argument, NULL, 'd' },
+               { "flags",      required_argument, NULL, 'f' },
+               { "help",       no_argument,       NULL, 'h' },
+               { "noflags",    no_argument,       NULL, 'F' },
+               { "noheadings", no_argument,       NULL, 'n' },
+               { "noident",    no_argument,       NULL, 'I' },
+               { "notimeouts", no_argument,       NULL, 'T' },
+               { "output",     required_argument, NULL, 'o' },
+               { "pairs",      no_argument,       NULL, 'P' },
+               { "raw",        no_argument,       NULL, 'r' },
+               { "version",    no_argument,       NULL, 'V' },
+               { NULL, 0, NULL, 0 }
+       };
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+       atexit(close_stdout);
+
+       while ((c = getopt_long(argc, argv,
+                               "d:f:hFnITo:PrV", long_opts, NULL)) != -1) {
+               switch(c) {
+               case 'd':
+                       wd.device = optarg;
+                       break;
+               case 'o':
+                       ncolumns = string_to_idarray(optarg,
+                                                    columns, ARRAY_SIZE(columns),
+                                                    column2id);
+                       if (ncolumns < 0)
+                               return EXIT_FAILURE;
+                       break;
+               case 'f':
+                       if (string_to_bitmask(optarg, (unsigned long *) &wanted, name2bit) != 0)
+                               return EXIT_FAILURE;
+                       break;
+               case 'V':
+                       printf(UTIL_LINUX_VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage(stdout);
+               case 'F':
+                       noflags = 1;
+                       break;
+               case 'I':
+                       noident = 1;
+                       break;
+               case 'T':
+                       notimeouts = 1;
+                       break;
+               case 'n':
+                       tt_flags |= TT_FL_NOHEADINGS;
+                       break;
+               case 'r':
+                       tt_flags |= TT_FL_RAW;
+                       break;
+               case 'P':
+                       tt_flags |= TT_FL_EXPORT;
+                       break;
+
+               case '?':
+               default:
+                       usage(stderr);
+               }
+       }
+
+       if (wanted && noflags)
+               errx(EXIT_FAILURE, _("--flags and --noflags are mutually exclusive"));
+
+       if (!ncolumns) {
+               /* default columns */
+               columns[ncolumns++] = COL_FLAG;
+               columns[ncolumns++] = COL_DESC;
+               columns[ncolumns++] = COL_STATUS;
+               columns[ncolumns++] = COL_BSTATUS;
+       }
+
+       if (optind < argc)
+               usage(stderr);
+
+       rc = read_watchdog(&wd);
+       if (rc)
+               goto done;
+
+       if (!noident)
+               printf(_("Identity: %s [version %x]\n"),
+                               wd.ident.identity,
+                               wd.ident.firmware_version);
+       if (!notimeouts)
+               show_timeouts(&wd);
+       if (!noflags && !(noident && notimeouts))
+               fputc('\n', stdout);
+       if (!noflags)
+               show_flags(&wd, tt_flags, wanted);
+done:
+       return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }