1  /*
       2   * wdctl(8) - show hardware watchdog status
       3   *
       4   * Copyright (C) 2012 Lennart Poettering
       5   * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
       6   *
       7   * This program is free software; you can redistribute it and/or modify it
       8   * under the terms of the GNU General Public License as published by the
       9   * Free Software Foundation; either version 2, or (at your option) any
      10   * later version.
      11   *
      12   * This program is distributed in the hope that it will be useful, but
      13   * WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      15   * General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU General Public License along
      18   * with this program; if not, write to the Free Software Foundation, Inc.,
      19   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20   */
      21  #include <sys/ioctl.h>
      22  #include <getopt.h>
      23  #include <stdio.h>
      24  #include <signal.h>
      25  #include <assert.h>
      26  #include <linux/watchdog.h>
      27  #include <sys/types.h>
      28  #include <sys/stat.h>
      29  #include <unistd.h>
      30  
      31  #include <libsmartcols.h>
      32  
      33  #include "nls.h"
      34  #include "c.h"
      35  #include "xalloc.h"
      36  #include "closestream.h"
      37  #include "optutils.h"
      38  #include "pathnames.h"
      39  #include "strutils.h"
      40  #include "carefulputc.h"
      41  #include "path.h"
      42  #include "strv.h"
      43  
      44  /*
      45   * since 2.6.18
      46   */
      47  #ifndef WDIOC_SETPRETIMEOUT
      48  # define WDIOC_SETPRETIMEOUT    _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
      49  # define WDIOC_GETPRETIMEOUT	_IOR(WATCHDOG_IOCTL_BASE, 9, int)
      50  # define WDIOC_GETTIMELEFT	_IOR(WATCHDOG_IOCTL_BASE, 10, int)
      51  # define WDIOF_POWEROVER	0x0040	/* Power over voltage */
      52  # define WDIOF_SETTIMEOUT	0x0080  /* Set timeout (in seconds) */
      53  # define WDIOF_MAGICCLOSE	0x0100	/* Supports magic close char */
      54  # define WDIOF_PRETIMEOUT	0x0200  /* Pretimeout (in seconds), get/set */
      55  # define WDIOF_KEEPALIVEPING	0x8000	/* Keep alive ping reply */
      56  #endif
      57  
      58  /*
      59   * since 3.5
      60   */
      61  #ifndef WDIOF_ALARMONLY
      62  # define WDIOF_ALARMONLY	0x0400	/* Watchdog triggers a management or
      63  					   other external alarm not a reboot */
      64  #endif
      65  
      66  struct wdflag {
      67  	uint32_t	flag;
      68  	const char	*name;
      69  	const char	*description;
      70  };
      71  
      72  static const struct wdflag wdflags[] = {
      73  	{ WDIOF_CARDRESET,     "CARDRESET",  N_("Card previously reset the CPU") },
      74  	{ WDIOF_EXTERN1,       "EXTERN1",    N_("External relay 1") },
      75  	{ WDIOF_EXTERN2,       "EXTERN2",    N_("External relay 2") },
      76  	{ WDIOF_FANFAULT,      "FANFAULT",   N_("Fan failed") },
      77  	{ WDIOF_KEEPALIVEPING, "KEEPALIVEPING", N_("Keep alive ping reply") },
      78  	{ WDIOF_MAGICCLOSE,    "MAGICCLOSE", N_("Supports magic close char") },
      79  	{ WDIOF_OVERHEAT,      "OVERHEAT",   N_("Reset due to CPU overheat") },
      80  	{ WDIOF_POWEROVER,     "POWEROVER",  N_("Power over voltage") },
      81  	{ WDIOF_POWERUNDER,    "POWERUNDER", N_("Power bad/power fault") },
      82  	{ WDIOF_PRETIMEOUT,    "PRETIMEOUT", N_("Pretimeout (in seconds)") },
      83  	{ WDIOF_SETTIMEOUT,    "SETTIMEOUT", N_("Set timeout (in seconds)") },
      84  	{ WDIOF_ALARMONLY,     "ALARMONLY",  N_("Not trigger reboot") }
      85  };
      86  
      87  
      88  /* column names */
      89  struct colinfo {
      90  	const char *name; /* header */
      91  	double	   whint; /* width hint (N < 1 is in percent of termwidth) */
      92  	int	   flags; /* SCOLS_FL_* */
      93  	const char *help;
      94  };
      95  
      96  enum { COL_FLAG, COL_DESC, COL_STATUS, COL_BSTATUS, COL_DEVICE };
      97  
      98  /* columns descriptions */
      99  static struct colinfo infos[] = {
     100  	[COL_FLAG]    = { "FLAG",        14,  0, N_("flag name") },
     101  	[COL_DESC]    = { "DESCRIPTION", 0.1, SCOLS_FL_TRUNC, N_("flag description") },
     102  	[COL_STATUS]  = { "STATUS",      1,   SCOLS_FL_RIGHT, N_("flag status") },
     103  	[COL_BSTATUS] = { "BOOT-STATUS", 1,   SCOLS_FL_RIGHT, N_("flag boot status") },
     104  	[COL_DEVICE]  = { "DEVICE",      0.1, 0, N_("watchdog device name") }
     105  
     106  };
     107  
     108  static int columns[ARRAY_SIZE(infos) * 2];
     109  static int ncolumns;
     110  
     111  struct wd_device {
     112  	const char	*devpath;
     113  	struct path_cxt	*sysfs;
     114  
     115  	char		*governor;
     116  	char		**available_governors;
     117  
     118  	int		timeout;
     119  	int		timeleft;
     120  	int		pretimeout;
     121  
     122  	uint32_t	status;
     123  	uint32_t	bstatus;
     124  	int		nowayout;
     125  
     126  	struct watchdog_info ident;
     127  
     128  	unsigned int	has_timeout : 1,
     129  			has_timeleft : 1,
     130  			has_pretimeout : 1,
     131  			has_nowayout : 1,
     132  			no_sysfs : 1;
     133  };
     134  
     135  struct wd_control {
     136  	/* set */
     137  	int		timeout;			/* --settimeout */
     138  	int		pretimeout;			/* --setpretimeout */
     139  	const char      *governor;			/* --setpregovernor */
     140  	unsigned int	set_timeout : 1,
     141  			set_pretimeout : 1;
     142  
     143  	/* output */
     144  	unsigned int	show_oneline : 1,
     145  			show_raw : 1,
     146  			hide_headings : 1,
     147  			hide_flags : 1,
     148  			hide_ident : 1,
     149  			hide_timeouts : 1;
     150  };
     151  
     152  #define want_set(_ctl)		((_ctl)->set_timeout \
     153  				  || (_ctl)->set_pretimeout \
     154  				  || (_ctl)->governor)
     155  
     156  /* converts flag name to flag bit */
     157  static long name2bit(const char *name, size_t namesz)
     158  {
     159  	size_t i;
     160  
     161  	for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
     162  		const char *cn = wdflags[i].name;
     163  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     164  			return wdflags[i].flag;
     165  	}
     166  	warnx(_("unknown flag: %s"), name);
     167  	return -1;
     168  }
     169  
     170  static int column2id(const char *name, size_t namesz)
     171  {
     172  	size_t i;
     173  
     174  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     175  		const char *cn = infos[i].name;
     176  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     177  			return i;
     178  	}
     179  	warnx(_("unknown column: %s"), name);
     180  	return -1;
     181  }
     182  
     183  static int get_column_id(int num)
     184  {
     185  	assert(num < ncolumns);
     186  	assert(columns[num] < (int) ARRAY_SIZE(infos));
     187  
     188  	return columns[num];
     189  }
     190  
     191  static struct colinfo *get_column_info(unsigned num)
     192  {
     193  	return &infos[ get_column_id(num) ];
     194  }
     195  
     196  /* We preffer cdev /dev/watchdog0 as this device has node in
     197   * /sys/class/watchdog/. The old miscdev /dev/watchdog is fallback for old
     198   * systemds only.
     199   */
     200  static const char *get_default_device(void)
     201  {
     202  	const char **p;
     203  	static const char *devs[] = {
     204  		"/dev/watchdog0",
     205  		"/dev/watchdog",
     206  		NULL
     207  	};
     208  
     209  	for (p = devs; *p; p++) {
     210  		if (access(*p, F_OK) == 0)
     211  			return *p;
     212  	}
     213  
     214  	return NULL;
     215  }
     216  
     217  static void __attribute__((__noreturn__)) usage(void)
     218  {
     219  	FILE *out = stdout;
     220  	size_t i;
     221  	const char *dflt = get_default_device();
     222  
     223  	fputs(USAGE_HEADER, out);
     224  	fprintf(out,
     225  	      _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
     226  
     227  	fputs(USAGE_SEPARATOR, out);
     228  	fputs(_("Show the status of the hardware watchdog.\n"), out);
     229  
     230  	fputs(USAGE_OPTIONS, out);
     231  	fputs(_(" -f, --flags <list>     print selected flags only\n"
     232  		" -F, --noflags          don't print information about flags\n"
     233  		" -I, --noident          don't print watchdog identity information\n"
     234  		" -n, --noheadings       don't print headings for flags table\n"
     235  		" -O, --oneline          print all information on one line\n"
     236  		" -o, --output <list>    output columns of the flags\n"
     237  		" -p, --setpretimeout <sec> set watchdog pre-timeout\n"
     238  		" -g, --setpregovernor <name> set pre-timeout governor\n"
     239  		" -r, --raw              use raw output format for flags table\n"
     240  		" -T, --notimeouts       don't print watchdog timeouts\n"
     241  		" -s, --settimeout <sec> set watchdog timeout\n"
     242  		" -x, --flags-only       print only flags table (same as -I -T)\n"), out);
     243  
     244  	fputs(USAGE_SEPARATOR, out);
     245  	printf(USAGE_HELP_OPTIONS(24));
     246  	fputs(USAGE_SEPARATOR, out);
     247  
     248  	if (dflt)
     249  		fprintf(out, _("The default device is %s.\n"), dflt);
     250  	else
     251  		fprintf(out, _("No default device is available.\n"));
     252  
     253  	fputs(USAGE_COLUMNS, out);
     254  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     255  		fprintf(out, " %13s  %s\n", infos[i].name, _(infos[i].help));
     256  
     257  	printf(USAGE_MAN_TAIL("wdctl(8)"));
     258  
     259  	exit(EXIT_SUCCESS);
     260  }
     261  
     262  static struct path_cxt *get_sysfs(struct wd_device *wd)
     263  {
     264  	struct path_cxt *sys;
     265  	struct stat st;
     266  
     267  	if (wd->no_sysfs)
     268  		return NULL;
     269  	if (wd->sysfs)
     270  		return wd->sysfs;
     271  	if (stat(wd->devpath, &st) != 0)
     272  		goto nosysfs;
     273  
     274  	sys = ul_new_path(_PATH_SYS_DEVCHAR "/%u:%u",
     275  			major(st.st_rdev), minor(st.st_rdev));
     276  	if (!sys)
     277  		return NULL;
     278  
     279  	if (ul_path_get_dirfd(sys) < 0)
     280  		goto nosysfs;		/* device not in /sys */
     281  
     282  	if (ul_path_access(sys, F_OK, "identity") != 0)
     283  		goto nosysfs;		/* no info in /sys (old miscdev?) */
     284  
     285  	wd->sysfs = sys;
     286  	return sys;
     287  nosysfs:
     288  	wd->no_sysfs = 1;
     289  	return NULL;
     290  }
     291  
     292  static void add_flag_line(struct libscols_table *table, struct wd_device *wd, const struct wdflag *fl)
     293  {
     294  	int i;
     295  	struct libscols_line *line;
     296  
     297  	line = scols_table_new_line(table, NULL);
     298  	if (!line) {
     299  		warn(_("failed to allocate output line"));
     300  		return;
     301  	}
     302  
     303  	for (i = 0; i < ncolumns; i++) {
     304  		const char *str = NULL;
     305  
     306  		switch (get_column_id(i)) {
     307  		case COL_FLAG:
     308  			str = fl->name;
     309  			break;
     310  		case COL_DESC:
     311  			str = fl->description;
     312  			break;
     313  		case COL_STATUS:
     314  			str = wd->status & fl->flag ? "1" : "0";
     315  			break;
     316  		case COL_BSTATUS:
     317  			str = wd->bstatus & fl->flag ? "1" : "0";
     318  			break;
     319  		case COL_DEVICE:
     320  			str = wd->devpath;
     321  			break;
     322  		default:
     323  			break;
     324  		}
     325  
     326  		if (str && scols_line_set_data(line, i, str)) {
     327  			warn(_("failed to add output data"));
     328  			break;
     329  		}
     330  	}
     331  }
     332  
     333  static int show_flags(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
     334  {
     335  	size_t i;
     336  	int rc = -1;
     337  	struct libscols_table *table;
     338  	uint32_t flags;
     339  
     340  	/* information about supported bits is probably missing in /sys */
     341  	if (!wd->ident.options)
     342  		return 0;
     343  
     344  	scols_init_debug(0);
     345  
     346  	/* create output table */
     347  	table = scols_new_table();
     348  	if (!table) {
     349  		warn(_("failed to allocate output table"));
     350  		return -1;
     351  	}
     352  	scols_table_enable_raw(table, ctl->show_raw);
     353  	scols_table_enable_noheadings(table, ctl->hide_headings);
     354  
     355  	/* define columns */
     356  	for (i = 0; i < (size_t) ncolumns; i++) {
     357  		struct colinfo *col = get_column_info(i);
     358  
     359  		if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
     360  			warnx(_("failed to allocate output column"));
     361  			goto done;
     362  		}
     363  	}
     364  
     365  	/* fill-in table with data
     366  	 * -- one line for each supported flag (option)	 */
     367  	flags = wd->ident.options;
     368  
     369  	for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
     370  		if (wanted && !(wanted & wdflags[i].flag))
     371  			; /* ignore */
     372  		else if (flags & wdflags[i].flag)
     373  			add_flag_line(table, wd, &wdflags[i]);
     374  
     375  		flags &= ~wdflags[i].flag;
     376  	}
     377  
     378  	if (flags)
     379  		warnx(_("%s: unknown flags 0x%x\n"), wd->devpath, flags);
     380  
     381  	scols_print_table(table);
     382  	rc = 0;
     383  done:
     384  	scols_unref_table(table);
     385  	return rc;
     386  }
     387  
     388  /*
     389   * Warning: successfully opened watchdog has to be properly closed with magic
     390   * close character otherwise the machine will be rebooted!
     391   *
     392   * Don't use err() or exit() here!
     393   */
     394  static int set_watchdog(struct wd_control *ctl, struct wd_device *wd)
     395  {
     396  	int fd;
     397  	sigset_t sigs, oldsigs;
     398  	int rc = 0;
     399  
     400  	assert(wd);
     401  	assert(wd->devpath);
     402  	assert(ctl);
     403  
     404  	if (!ctl->set_timeout && !ctl->set_timeout)
     405  		goto sysfs_only;
     406  
     407  	sigemptyset(&oldsigs);
     408  	sigfillset(&sigs);
     409  	sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
     410  
     411  	fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
     412  
     413  	if (fd < 0) {
     414  		if (errno == EBUSY)
     415  			warnx(_("%s: watchdog already in use, terminating."),
     416  					wd->devpath);
     417  		warn(_("cannot open %s"), wd->devpath);
     418  		return -1;
     419  	}
     420  
     421  	for (;;) {
     422  		/* We just opened this to query the state, not to arm
     423  		 * it hence use the magic close character */
     424  		static const char v = 'V';
     425  
     426  		if (write(fd, &v, 1) >= 0)
     427  			break;
     428  		if (errno != EINTR) {
     429  			warn(_("%s: failed to disarm watchdog"), wd->devpath);
     430  			break;
     431  		}
     432  		/* Let's try hard, since if we don't get this right
     433  		 * the machine might end up rebooting. */
     434  	}
     435  
     436  	if (ctl->set_timeout) {
     437  		if (ioctl(fd, WDIOC_SETTIMEOUT, &ctl->timeout) != 0) {
     438  			rc += errno;
     439  			warn(_("cannot set timeout for %s"), wd->devpath);
     440  		} else
     441  			printf(P_("Timeout has been set to %d second.\n",
     442  				  "Timeout has been set to %d seconds.\n",
     443  				  ctl->timeout), ctl->timeout);
     444  	}
     445  
     446  	if (ctl->set_pretimeout) {
     447  		if (ioctl(fd, WDIOC_SETPRETIMEOUT, &ctl->pretimeout) != 0) {
     448  			rc += errno;
     449  			warn(_("cannot set pretimeout for %s"), wd->devpath);
     450  		} else
     451  			printf(P_("Pre-timeout has been set to %d second.\n",
     452  				  "Pre-timeout has been set to %d seconds.\n",
     453  				  ctl->pretimeout), ctl->pretimeout);
     454  	}
     455  
     456  	if (close(fd))
     457  		warn(_("write failed"));
     458  
     459  	sigprocmask(SIG_SETMASK, &oldsigs, NULL);
     460  
     461  sysfs_only:
     462  	if (ctl->governor) {
     463  		struct path_cxt *sys = get_sysfs(wd);
     464  		int xrc;
     465  
     466  		xrc = !sys ? errno :
     467  			     ul_path_write_string(sys, ctl->governor,
     468  						"pretimeout_governor");
     469  		if (xrc)
     470  			warn(_("cannot set pre-timeout governor"));
     471  		rc += xrc;
     472  	}
     473  
     474  	return rc;
     475  }
     476  
     477  /*
     478   * Warning: successfully opened watchdog has to be properly closed with magic
     479   * close character otherwise the machine will be rebooted!
     480   *
     481   * Don't use err() or exit() here!
     482   */
     483  static int read_watchdog_from_device(struct wd_device *wd)
     484  {
     485  	int fd;
     486  	sigset_t sigs, oldsigs;
     487  
     488  	assert(wd->devpath);
     489  
     490  	sigemptyset(&oldsigs);
     491  	sigfillset(&sigs);
     492  	sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
     493  
     494  	fd = open(wd->devpath, O_WRONLY|O_CLOEXEC);
     495  
     496  	if (fd < 0)
     497  		return -errno;
     498  
     499  	if (ioctl(fd, WDIOC_GETSUPPORT, &wd->ident) < 0)
     500  		warn(_("%s: failed to get information about watchdog"), wd->devpath);
     501  	else {
     502  		ioctl(fd, WDIOC_GETSTATUS, &wd->status);
     503  		ioctl(fd, WDIOC_GETBOOTSTATUS, &wd->bstatus);
     504  
     505  		/*
     506  		 * Sometimes supported options like WDIOF_CARDRESET are missing from
     507  		 * ident.options, add anything set in status/bstatus to ident.options.
     508  		 */
     509  		wd->ident.options |= wd->status;
     510  		wd->ident.options |= wd->bstatus;
     511  
     512  		if (ioctl(fd, WDIOC_GETTIMEOUT, &wd->timeout) >= 0)
     513  			wd->has_timeout = 1;
     514  		if (ioctl(fd, WDIOC_GETPRETIMEOUT, &wd->pretimeout) >= 0)
     515  			wd->has_pretimeout = 1;
     516  		if (ioctl(fd, WDIOC_GETTIMELEFT, &wd->timeleft) >= 0)
     517  			wd->has_timeleft = 1;
     518  	}
     519  
     520  	for (;;) {
     521  		/* We just opened this to query the state, not to arm
     522  		 * it hence use the magic close character */
     523  		static const char v = 'V';
     524  
     525  		if (write(fd, &v, 1) >= 0)
     526  			break;
     527  		if (errno != EINTR) {
     528  			warn(_("%s: failed to disarm watchdog"), wd->devpath);
     529  			break;
     530  		}
     531  		/* Let's try hard, since if we don't get this right
     532  		 * the machine might end up rebooting. */
     533  	}
     534  
     535  	if (close(fd))
     536  		warn(_("write failed"));
     537  	sigprocmask(SIG_SETMASK, &oldsigs, NULL);
     538  
     539  	return 0;
     540  }
     541  
     542  
     543  /* Returns: <0 error, 0 success, 1 unssuported */
     544  static int read_watchdog_from_sysfs(struct wd_device *wd)
     545  {
     546  	struct path_cxt *sys;
     547  
     548  	sys = get_sysfs(wd);
     549  	if (!sys)
     550  		return 1;
     551  
     552  	ul_path_read_buffer(sys, (char *) wd->ident.identity, sizeof(wd->ident.identity), "identity");
     553  	ul_path_read_u32(sys, &wd->ident.firmware_version, "fw_version");
     554  	ul_path_scanf(sys, "options", "%x", &wd->ident.options);
     555  
     556  	ul_path_scanf(sys, "status", "%x", &wd->status);
     557  	ul_path_read_u32(sys, &wd->bstatus, "bootstatus");
     558  
     559  	if (ul_path_read_s32(sys, &wd->nowayout, "nowayout") == 0)
     560  		wd->has_nowayout = 1;
     561  	if (ul_path_read_s32(sys, &wd->timeout, "timeout") == 0)
     562  		wd->has_timeout = 1;
     563  	if (ul_path_read_s32(sys, &wd->pretimeout, "pretimeout") == 0)
     564  		wd->has_pretimeout = 1;
     565  	if (ul_path_read_s32(sys, &wd->timeleft, "timeleft") == 0)
     566  		wd->has_timeleft = 1;
     567  
     568  	return 0;
     569  }
     570  
     571  static int read_governors(struct wd_device *wd)
     572  {
     573  	struct path_cxt *sys;
     574  	FILE *f;
     575  
     576  	sys = get_sysfs(wd);
     577  	if (!sys)
     578  		return 1;
     579  
     580  	f = ul_path_fopen(sys, "r", "pretimeout_available_governors");
     581  	if (f) {
     582  		char *line = NULL;
     583  		size_t dummy = 0;
     584  		ssize_t sz;
     585  
     586  		while ((sz = getline(&line, &dummy, f)) >= 0) {
     587  			if (rtrim_whitespace((unsigned char *) line) == 0)
     588  				continue;
     589  			strv_consume(&wd->available_governors, line);
     590  			dummy = 0;
     591  			line = NULL;
     592  		}
     593  		free(line);
     594  		fclose(f);
     595  	}
     596  
     597  	ul_path_read_string(sys, &wd->governor,  "pretimeout_governor");
     598  	return 0;
     599  }
     600  
     601  static int read_watchdog(struct wd_device *wd)
     602  {
     603  	int rc = read_watchdog_from_device(wd);
     604  
     605  	if (rc == -EBUSY || rc == -EACCES || rc == -EPERM)
     606  		rc = read_watchdog_from_sysfs(wd);
     607  
     608  	if (rc) {
     609  		warn(_("cannot read information about %s"), wd->devpath);
     610  		return -1;
     611  	}
     612  
     613  	read_governors(wd);
     614  	return 0;
     615  }
     616  
     617  static void show_timeouts(struct wd_device *wd)
     618  {
     619  	if (wd->has_timeout)
     620  		printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeout),
     621  			  _("Timeout:"), wd->timeout);
     622  	if (wd->has_timeleft)
     623  		printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->timeleft),
     624  			  _("Timeleft:"), wd->timeleft);
     625  	if (wd->has_pretimeout)
     626  		printf(P_("%-14s %2i second\n", "%-14s %2i seconds\n", wd->pretimeout),
     627  			  _("Pre-timeout:"), wd->pretimeout);
     628  }
     629  
     630  static void show_governors(struct wd_device *wd)
     631  {
     632  	if (wd->governor)
     633  		printf(_("%-14s %s\n"), _("Pre-timeout governor:"), wd->governor);
     634  	if (wd->available_governors) {
     635  		char *tmp = strv_join(wd->available_governors, " ");
     636  
     637  		if (tmp)
     638  			printf(_("%-14s %s\n"),
     639  				_("Available pre-timeout governors:"), tmp);
     640  		free(tmp);
     641  	}
     642  }
     643  
     644  static void print_oneline(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
     645  {
     646  	printf("%s:", wd->devpath);
     647  
     648  	if (!ctl->hide_ident) {
     649  		printf(" VERSION=\"%x\"", wd->ident.firmware_version);
     650  
     651  		printf(" IDENTITY=");
     652  		fputs_quoted((char *) wd->ident.identity, stdout);
     653  	}
     654  	if (!ctl->hide_timeouts) {
     655  		if (wd->has_timeout)
     656  			printf(" TIMEOUT=\"%i\"", wd->timeout);
     657  		if (wd->has_pretimeout)
     658  			printf(" PRETIMEOUT=\"%i\"", wd->pretimeout);
     659  		if (wd->has_timeleft)
     660  			printf(" TIMELEFT=\"%i\"", wd->timeleft);
     661  	}
     662  
     663  	if (!ctl->hide_flags) {
     664  		size_t i;
     665  		uint32_t flags = wd->ident.options;
     666  
     667  		for (i = 0; i < ARRAY_SIZE(wdflags); i++) {
     668  			const struct wdflag *fl;
     669  
     670  			if ((wanted && !(wanted & wdflags[i].flag)) ||
     671  			    !(flags & wdflags[i].flag))
     672  				continue;
     673  
     674  			fl= &wdflags[i];
     675  
     676  			printf(" %s=\"%s\"", fl->name,
     677  					     wd->status & fl->flag ? "1" : "0");
     678  			printf(" %s_BOOT=\"%s\"", fl->name,
     679  					     wd->bstatus & fl->flag ? "1" : "0");
     680  
     681  		}
     682  	}
     683  
     684  	fputc('\n', stdout);
     685  }
     686  
     687  static void print_device(struct wd_control *ctl, struct wd_device *wd, uint32_t wanted)
     688  {
     689  	/* NAME=value one line output */
     690  	if (ctl->show_oneline) {
     691  		print_oneline(ctl, wd, wanted);
     692  		return;
     693  	}
     694  
     695  	/* pretty output */
     696  	if (!ctl->hide_ident) {
     697  		printf("%-15s%s\n", _("Device:"), wd->devpath);
     698  		printf("%-15s%s [%s %x]\n",
     699  				_("Identity:"),
     700  				wd->ident.identity,
     701  				_("version"),
     702  				wd->ident.firmware_version);
     703  	}
     704  	if (!ctl->hide_timeouts)
     705  		show_timeouts(wd);
     706  
     707  	show_governors(wd);
     708  
     709  	if (!ctl->hide_flags)
     710  		show_flags(ctl, wd, wanted);
     711  }
     712  
     713  int main(int argc, char *argv[])
     714  {
     715  	struct wd_device wd;
     716  	struct wd_control ctl = { .hide_headings = 0 };
     717  	int c, res = EXIT_SUCCESS, count = 0;
     718  	unsigned long wanted = 0;
     719  	const char *dflt_device = NULL;
     720  
     721  	static const struct option long_opts[] = {
     722  		{ "flags",      required_argument, NULL, 'f' },
     723  		{ "flags-only", no_argument,       NULL, 'x' },
     724  		{ "help",	no_argument,       NULL, 'h' },
     725  		{ "noflags",    no_argument,       NULL, 'F' },
     726  		{ "noheadings", no_argument,       NULL, 'n' },
     727  		{ "noident",	no_argument,       NULL, 'I' },
     728  		{ "notimeouts", no_argument,       NULL, 'T' },
     729  		{ "settimeout", required_argument, NULL, 's' },
     730  		{ "setpretimeout", required_argument, NULL, 'p' },
     731  		{ "setpregovernor", required_argument, NULL, 'g' },
     732  		{ "output",     required_argument, NULL, 'o' },
     733  		{ "oneline",    no_argument,       NULL, 'O' },
     734  		{ "raw",        no_argument,       NULL, 'r' },
     735  		{ "version",    no_argument,       NULL, 'V' },
     736  		{ NULL, 0, NULL, 0 }
     737  	};
     738  
     739  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     740  		{ 'F','f' },			/* noflags,flags*/
     741  		{ 0 }
     742  	};
     743  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     744  
     745  	setlocale(LC_ALL, "");
     746  	bindtextdomain(PACKAGE, LOCALEDIR);
     747  	textdomain(PACKAGE);
     748  	close_stdout_atexit();
     749  
     750  	while ((c = getopt_long(argc, argv,
     751  				"d:f:g:hFnITp:o:s:OrVx", long_opts, NULL)) != -1) {
     752  
     753  		err_exclusive_options(c, long_opts, excl, excl_st);
     754  
     755  		switch(c) {
     756  		case 'o':
     757  			ncolumns = string_to_idarray(optarg,
     758  						     columns, ARRAY_SIZE(columns),
     759  						     column2id);
     760  			if (ncolumns < 0)
     761  				return EXIT_FAILURE;
     762  			break;
     763  		case 's':
     764  			ctl.timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
     765  			ctl.set_timeout = 1;
     766  			break;
     767  		case 'p':
     768  			ctl.pretimeout = strtos32_or_err(optarg, _("invalid pretimeout argument"));
     769  			ctl.set_pretimeout = 1;
     770  			break;
     771  		case 'f':
     772  			if (string_to_bitmask(optarg, &wanted, name2bit) != 0)
     773  				return EXIT_FAILURE;
     774  			break;
     775  		case 'F':
     776  			ctl.hide_flags = 1;
     777  			break;
     778  		case 'g':
     779  			ctl.governor = optarg;
     780  			break;
     781  		case 'I':
     782  			ctl.hide_ident = 1;
     783  			break;
     784  		case 'T':
     785  			ctl.hide_timeouts = 1;
     786  			break;
     787  		case 'n':
     788  			ctl.hide_headings = 1;
     789  			break;
     790  		case 'r':
     791  			ctl.show_raw = 1;
     792  			break;
     793  		case 'O':
     794  			ctl.show_oneline = 1;
     795  			break;
     796  		case 'x':
     797  			ctl.hide_ident = 1;
     798  			ctl.hide_timeouts = 1;
     799  			break;
     800  
     801  		case 'h':
     802  			usage();
     803  		case 'V':
     804  			print_version(EXIT_SUCCESS);
     805  		default:
     806  			errtryhelp(EXIT_FAILURE);
     807  		}
     808  	}
     809  
     810  	if (!ncolumns) {
     811  		/* default columns */
     812  		columns[ncolumns++] = COL_FLAG;
     813  		columns[ncolumns++] = COL_DESC;
     814  		columns[ncolumns++] = COL_STATUS;
     815  		columns[ncolumns++] = COL_BSTATUS;
     816  	}
     817  
     818  	/* Device no specified, use default. */
     819  	if (optind == argc) {
     820  		dflt_device = get_default_device();
     821  		if (!dflt_device)
     822  			err(EXIT_FAILURE, _("No default device is available."));
     823  	}
     824  
     825  	do {
     826  		int rc;
     827  
     828  		memset(&wd, 0, sizeof(wd));
     829  		wd.devpath = dflt_device ? dflt_device : argv[optind++];
     830  
     831  		if (count)
     832  			fputc('\n', stdout);
     833  		count++;
     834  
     835  		if (want_set(&ctl)) {
     836  			rc = set_watchdog(&ctl, &wd);
     837  			if (rc) {
     838  				res = EXIT_FAILURE;
     839  			}
     840  		}
     841  
     842  		rc = read_watchdog(&wd);
     843  		if (rc) {
     844  			res = EXIT_FAILURE;
     845  			continue;
     846  		}
     847  
     848  		print_device(&ctl, &wd, wanted);
     849  		ul_unref_path(wd.sysfs);
     850  	} while (optind < argc);
     851  
     852  	return res;
     853  }