(root)/
util-linux-2.39/
sys-utils/
rfkill.c
       1  /*
       2   * /dev/rfkill userspace tool
       3   *
       4   * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
       5   * Copyright 2009 Marcel Holtmann <marcel@holtmann.org>
       6   * Copyright 2009 Tim Gardner <tim.gardner@canonical.com>
       7   * Copyright 2017 Sami Kerola <kerolasa@iki.fi>
       8   * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
       9   *
      10   * Permission to use, copy, modify, and/or distribute this software for any
      11   * purpose with or without fee is hereby granted, provided that the above
      12   * copyright notice and this permission notice appear in all copies.
      13   *
      14   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      15   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      16   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      17   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      18   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      19   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      20   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      21   */
      22  
      23  #include <ctype.h>
      24  #include <getopt.h>
      25  #include <libsmartcols.h>
      26  #include <linux/rfkill.h>
      27  #include <poll.h>
      28  #include <sys/syslog.h>
      29  #include <sys/time.h>
      30  
      31  #include "c.h"
      32  #include "closestream.h"
      33  #include "nls.h"
      34  #include "optutils.h"
      35  #include "pathnames.h"
      36  #include "strutils.h"
      37  #include "timeutils.h"
      38  #include "widechar.h"
      39  #include "xalloc.h"
      40  #include "all-io.h"
      41  
      42  
      43  /*
      44   * NFC supported by kernel since v3.10 (year 2013); FM and another types are from
      45   * year 2009 (2.6.33) or older.
      46   */
      47  #ifndef RFKILL_TYPE_NFC
      48  # ifndef RFKILL_TYPE_FM
      49  #  define RFKILL_TYPE_FM	RFKILL_TYPE_GPS + 1
      50  # endif
      51  # define RFKILL_TYPE_NFC	RFKILL_TYPE_FM + 1
      52  # undef NUM_RFKILL_TYPES
      53  # define NUM_RFKILL_TYPES	RFKILL_TYPE_NFC + 1
      54  #endif
      55  
      56  struct rfkill_type_str {
      57  	enum rfkill_type type;	/* ID */
      58  	const char *name;	/* generic name */
      59  	const char *desc;	/* human readable name */
      60  };
      61  
      62  static const struct rfkill_type_str rfkill_type_strings[] = {
      63  	{ .type = RFKILL_TYPE_ALL,       .name = "all"           },
      64  	{ .type = RFKILL_TYPE_WLAN,      .name = "wlan",         .desc = "Wireless LAN" },
      65  	{ .type = RFKILL_TYPE_WLAN,      .name = "wifi"          },				/* alias */
      66  	{ .type = RFKILL_TYPE_BLUETOOTH, .name = "bluetooth",    .desc = "Bluetooth" },
      67  	{ .type = RFKILL_TYPE_UWB,       .name = "uwb",          .desc = "Ultra-Wideband" },
      68  	{ .type = RFKILL_TYPE_UWB,       .name = "ultrawideband" }, /* alias */
      69  	{ .type = RFKILL_TYPE_WIMAX,     .name = "wimax",        .desc = "WiMAX" },
      70  	{ .type = RFKILL_TYPE_WWAN,      .name = "wwan",         .desc = "Wireless WAN" },
      71  	{ .type = RFKILL_TYPE_GPS,       .name = "gps",          .desc = "GPS" },
      72  	{ .type = RFKILL_TYPE_FM,        .name = "fm",           .desc = "FM" },
      73  	{ .type = RFKILL_TYPE_NFC,       .name = "nfc",          .desc = "NFC" },
      74  	{ .type = NUM_RFKILL_TYPES,      .name = NULL            }
      75  };
      76  
      77  struct rfkill_id {
      78  	union {
      79  		enum rfkill_type type;
      80  		uint32_t index;
      81  	};
      82  	enum {
      83  		RFKILL_IS_INVALID,
      84  		RFKILL_IS_TYPE,
      85  		RFKILL_IS_INDEX,
      86  		RFKILL_IS_ALL
      87  	} result;
      88  };
      89  
      90  /* supported actions */
      91  enum {
      92  	ACT_LIST,
      93  	ACT_HELP,
      94  	ACT_EVENT,
      95  	ACT_BLOCK,
      96  	ACT_UNBLOCK,
      97  	ACT_TOGGLE,
      98  
      99  	ACT_LIST_OLD
     100  };
     101  
     102  static char *rfkill_actions[] = {
     103  	[ACT_LIST]	= "list",
     104  	[ACT_HELP]	= "help",
     105  	[ACT_EVENT]	= "event",
     106  	[ACT_BLOCK]	= "block",
     107  	[ACT_UNBLOCK]	= "unblock",
     108  	[ACT_TOGGLE]	= "toggle"
     109  };
     110  
     111  /* column IDs */
     112  enum {
     113  	COL_DEVICE,
     114  	COL_ID,
     115  	COL_TYPE,
     116  	COL_DESC,
     117  	COL_SOFT,
     118  	COL_HARD
     119  };
     120  
     121  /* column names */
     122  struct colinfo {
     123  	const char *name;	/* header */
     124  	double whint;		/* width hint (N < 1 is in percent of termwidth) */
     125  	int flags;		/* SCOLS_FL_* */
     126  	const char *help;
     127  };
     128  
     129  /* columns descriptions */
     130  static const struct colinfo infos[] = {
     131  	[COL_DEVICE] = {"DEVICE", 0, 0, N_("kernel device name")},
     132  	[COL_ID]     = {"ID",	  2, SCOLS_FL_RIGHT, N_("device identifier value")},
     133  	[COL_TYPE]   = {"TYPE",	  0, 0, N_("device type name that can be used as identifier")},
     134  	[COL_DESC]   = {"TYPE-DESC",   0, 0, N_("device type description")},
     135  	[COL_SOFT]   = {"SOFT",	  0, SCOLS_FL_RIGHT, N_("status of software block")},
     136  	[COL_HARD]   = {"HARD",	  0, SCOLS_FL_RIGHT, N_("status of hardware block")}
     137  };
     138  
     139  static int columns[ARRAY_SIZE(infos) * 2];
     140  static size_t ncolumns;
     141  
     142  struct control {
     143  	struct libscols_table *tb;
     144  	unsigned int
     145  		json:1,
     146  		no_headings:1,
     147  		raw:1;
     148  };
     149  
     150  static int column_name_to_id(const char *name, size_t namesz)
     151  {
     152  	size_t i;
     153  
     154  	assert(name);
     155  
     156  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     157  		const char *cn = infos[i].name;
     158  
     159  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     160  			return i;
     161  	}
     162  	warnx(_("unknown column: %s"), name);
     163  	return -1;
     164  }
     165  
     166  static int get_column_id(size_t num)
     167  {
     168  	assert(num < ncolumns);
     169  	assert(columns[num] < (int)ARRAY_SIZE(infos));
     170  	return columns[num];
     171  }
     172  
     173  static const struct colinfo *get_column_info(int num)
     174  {
     175  	return &infos[get_column_id(num)];
     176  }
     177  
     178  static int string_to_action(const char *str)
     179  {
     180  	size_t i;
     181  
     182  	for (i = 0; i < ARRAY_SIZE(rfkill_actions); i++)
     183  		if (strcmp(str, rfkill_actions[i]) == 0)
     184  			return i;
     185  
     186  	return -EINVAL;
     187  }
     188  
     189  static int rfkill_open(int rdonly, int nonblock)
     190  {
     191  	int fd;
     192  
     193  	fd = open(_PATH_DEV_RFKILL, rdonly ? O_RDONLY : O_RDWR);
     194  	if (fd < 0) {
     195  		warn(_("cannot open %s"), _PATH_DEV_RFKILL);
     196  		return -errno;
     197  	}
     198  
     199  	if (nonblock && fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
     200  		warn(_("cannot set non-blocking %s"), _PATH_DEV_RFKILL);
     201  		close(fd);
     202  		return -errno;
     203  	}
     204  
     205  	return fd;
     206  }
     207  
     208  /* returns: 0 success, 1 read again, < 0 error */
     209  static int rfkill_read_event(int fd, struct rfkill_event *event)
     210  {
     211  	ssize_t	len = read(fd, event, sizeof(*event));
     212  
     213  	if (len < 0) {
     214  		if (errno == EAGAIN)
     215  			return 1;
     216  		warn(_("cannot read %s"), _PATH_DEV_RFKILL);
     217  		return -errno;
     218  	}
     219  
     220  	if ((size_t) len < (size_t) RFKILL_EVENT_SIZE_V1) {
     221  		warnx(_("wrong size of rfkill event: %zu < %zu"),
     222  				(size_t) len, (size_t) RFKILL_EVENT_SIZE_V1);
     223  		return 1;
     224  	}
     225  
     226  	return 0;
     227  }
     228  
     229  
     230  static int rfkill_event(void)
     231  {
     232  	enum {
     233  		POLLFD_RFKILL,
     234  		POLLFD_STDOUT,
     235  		POLLFD_COUNT
     236  	};
     237  	struct rfkill_event event;
     238  	struct timeval tv;
     239  	char date_buf[ISO_BUFSIZ];
     240  	struct pollfd p[POLLFD_COUNT];
     241  	int fd, n;
     242  
     243  	fd = rfkill_open(1, 0);
     244  	if (fd < 0)
     245  		return fd;
     246  
     247  	memset(&p, 0, sizeof(p));
     248  	p[POLLFD_RFKILL].fd = fd;
     249  	p[POLLFD_RFKILL].events = POLLIN | POLLHUP;
     250  	p[POLLFD_STDOUT].fd = STDOUT_FILENO;
     251  	p[POLLFD_STDOUT].events = 0;
     252  
     253  	/* interrupted by signal only */
     254  	while (1) {
     255  		int rc = 1;	/* recover-able error */
     256  
     257  		n = poll(p, ARRAY_SIZE(p), -1);
     258  		if (n < 0) {
     259  			warn(_("failed to poll %s"), _PATH_DEV_RFKILL);
     260  			goto failed;
     261  		}
     262  
     263  		if (p[POLLFD_STDOUT].revents)
     264  			goto failed; /* read end of stdout closed */
     265  		if (p[POLLFD_RFKILL].revents)
     266  			rc = rfkill_read_event(fd, &event);
     267  		if (rc < 0)
     268  			goto failed;
     269  		if (rc)
     270  			continue;
     271  
     272  		gettimeofday(&tv, NULL);
     273  		strtimeval_iso(&tv, ISO_TIMESTAMP_COMMA, date_buf,
     274  			       sizeof(date_buf));
     275  		printf("%s: idx %u type %u op %u soft %u hard %u\n",
     276  		       date_buf,
     277  		       event.idx, event.type, event.op, event.soft, event.hard);
     278  		fflush(stdout);
     279  	}
     280  
     281  failed:
     282  	close(fd);
     283  	return -1;
     284  }
     285  
     286  static const char *get_sys_attr(uint32_t idx, const char *attr)
     287  {
     288  	static char name[128];
     289  	char path[PATH_MAX];
     290  	FILE *f;
     291  	char *p;
     292  
     293  	snprintf(path, sizeof(path), _PATH_SYS_RFKILL "/rfkill%u/%s", idx, attr);
     294  	f = fopen(path, "r");
     295  	if (!f)
     296  		goto done;
     297  	if (!fgets(name, sizeof(name), f))
     298  		goto done;
     299  	p = strchr(name, '\n');
     300  	if (p)
     301  		*p = '\0';
     302  done:
     303  	if (f)
     304  		fclose(f);
     305  	return name;
     306  }
     307  
     308  static struct rfkill_id rfkill_id_to_type(const char *s)
     309  {
     310  	const struct rfkill_type_str *p;
     311  	struct rfkill_id ret = { .result = 0 };
     312  
     313  	if (islower(*s)) {
     314  		for (p = rfkill_type_strings; p->name != NULL; p++) {
     315  			if (!strcmp(s, p->name)) {
     316  				ret.type = p->type;
     317  				if (!strcmp(s, "all"))
     318  					ret.result = RFKILL_IS_ALL;
     319  				else
     320  					ret.result = RFKILL_IS_TYPE;
     321  				return ret;
     322  			}
     323  		}
     324  	} else if (isdigit(*s)) {
     325  		/* assume a numeric character implies an index. */
     326  		char filename[64];
     327  
     328  		ret.index = strtou32_or_err(s, _("invalid identifier"));
     329  		snprintf(filename, sizeof(filename) - 1,
     330  			 _PATH_SYS_RFKILL "/rfkill%" PRIu32 "/name", ret.index);
     331  		if (access(filename, F_OK) == 0)
     332  			ret.result = RFKILL_IS_INDEX;
     333  		else
     334  			ret.result = RFKILL_IS_INVALID;
     335  		return ret;
     336  	}
     337  
     338  	ret.result = RFKILL_IS_INVALID;
     339  	return ret;
     340  }
     341  
     342  static const char *rfkill_type_to_desc(enum rfkill_type type)
     343  {
     344  	size_t i;
     345  
     346  	for (i = 0; i < ARRAY_SIZE(rfkill_type_strings); i++) {
     347  		if (type == rfkill_type_strings[i].type)
     348  			return rfkill_type_strings[i].desc;
     349  	}
     350  
     351  	return NULL;
     352  }
     353  
     354  
     355  static int event_match(struct rfkill_event *event, struct rfkill_id *id)
     356  {
     357  	if (event->op != RFKILL_OP_ADD)
     358  		return 0;
     359  
     360  	/* filter out unwanted results */
     361  	switch (id->result) {
     362  	case RFKILL_IS_TYPE:
     363  		if (event->type != id->type)
     364  			return 0;
     365  		break;
     366  	case RFKILL_IS_INDEX:
     367  		if (event->idx != id->index)
     368  			return 0;
     369  		break;
     370  	case RFKILL_IS_ALL:
     371  		break;
     372  	default:
     373  		abort();
     374  	}
     375  
     376  	return 1;
     377  }
     378  
     379  static void fill_table_row(struct libscols_table *tb, struct rfkill_event *event)
     380  {
     381  	static struct libscols_line *ln;
     382  	size_t i;
     383  
     384  	assert(tb);
     385  
     386  	ln = scols_table_new_line(tb, NULL);
     387  	if (!ln) {
     388  		errno = ENOMEM;
     389  		errx(EXIT_FAILURE, _("failed to allocate output line"));
     390  	}
     391  
     392  	for (i = 0; i < (size_t)ncolumns; i++) {
     393  		char *str = NULL;
     394  		switch (get_column_id(i)) {
     395  		case COL_DEVICE:
     396  			str = xstrdup(get_sys_attr(event->idx, "name"));
     397  			break;
     398  		case COL_ID:
     399  			xasprintf(&str, "%" PRIu32, event->idx);
     400  			break;
     401  		case COL_TYPE:
     402  			str = xstrdup(get_sys_attr(event->idx, "type"));
     403  			break;
     404  		case COL_DESC:
     405  			str = xstrdup(rfkill_type_to_desc(event->type));
     406  			break;
     407  		case COL_SOFT:
     408  			str = xstrdup(event->soft ? _("blocked") : _("unblocked"));
     409  			break;
     410  		case COL_HARD:
     411  			str = xstrdup(event->hard ? _("blocked") : _("unblocked"));
     412  			break;
     413  		default:
     414  			abort();
     415  		}
     416  		if (str && scols_line_refer_data(ln, i, str))
     417  			errx(EXIT_FAILURE, _("failed to add output data"));
     418  	}
     419  }
     420  
     421  static int rfkill_list_old(const char *param)
     422  {
     423  	struct rfkill_id id = { .result = RFKILL_IS_ALL };
     424  	struct rfkill_event event;
     425  	int fd, rc = 0;
     426  
     427  	if (param) {
     428  		id = rfkill_id_to_type(param);
     429  		if (id.result == RFKILL_IS_INVALID) {
     430  			warnx(_("invalid identifier: %s"), param);
     431  			return -EINVAL;
     432  		}
     433  	}
     434  
     435  	fd = rfkill_open(1, 1);
     436  	if (fd < 0)
     437  		return fd;
     438  
     439  	while (1) {
     440  		rc = rfkill_read_event(fd, &event);
     441  		if (rc < 0)
     442  			break;
     443  		if (rc == 1 && errno == EAGAIN) {
     444  			rc = 0;		/* done */
     445  			break;
     446  		}
     447  		if (rc == 0 && event_match(&event, &id)) {
     448  			char *name = xstrdup(get_sys_attr(event.idx, "name")),
     449  			     *type = xstrdup(rfkill_type_to_desc(event.type));
     450  
     451  			if (!type)
     452  				type = xstrdup(get_sys_attr(event.idx, "type"));
     453  
     454  			printf("%u: %s: %s\n", event.idx, name, type);
     455  			printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no");
     456  			printf("\tHard blocked: %s\n", event.hard ? "yes" : "no");
     457  
     458  			free(name);
     459  			free(type);
     460  		}
     461  	}
     462  	close(fd);
     463  	return rc;
     464  }
     465  
     466  static void rfkill_list_init(struct control *ctrl)
     467  {
     468  	size_t i;
     469  
     470  	scols_init_debug(0);
     471  
     472  	ctrl->tb = scols_new_table();
     473  	if (!ctrl->tb)
     474  		err(EXIT_FAILURE, _("failed to allocate output table"));
     475  
     476  	scols_table_enable_json(ctrl->tb, ctrl->json);
     477  	scols_table_set_name(ctrl->tb, "rfkilldevices");
     478  	scols_table_enable_noheadings(ctrl->tb, ctrl->no_headings);
     479  	scols_table_enable_raw(ctrl->tb, ctrl->raw);
     480  
     481  	for (i = 0; i < (size_t) ncolumns; i++) {
     482  		const struct colinfo *col = get_column_info(i);
     483  		struct libscols_column *cl;
     484  
     485  		cl = scols_table_new_column(ctrl->tb, col->name, col->whint, col->flags);
     486  		if (!cl)
     487  			err(EXIT_FAILURE, _("failed to allocate output column"));
     488  		if (ctrl->json) {
     489  			int id = get_column_id(i);
     490  			if (id == COL_ID)
     491  				scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
     492  		}
     493  	}
     494  }
     495  
     496  static int rfkill_list_fill(struct control const *ctrl, const char *param)
     497  {
     498  	struct rfkill_id id = { .result = RFKILL_IS_ALL };
     499  	struct rfkill_event event;
     500  	int fd, rc = 0;
     501  
     502  	if (param) {
     503  		id = rfkill_id_to_type(param);
     504  		if (id.result == RFKILL_IS_INVALID) {
     505  			warnx(_("invalid identifier: %s"), param);
     506  			return -EINVAL;
     507  		}
     508  	}
     509  
     510  	fd = rfkill_open(1, 1);
     511  	if (fd < 0)
     512  		return fd;
     513  
     514  	while (1) {
     515  		rc = rfkill_read_event(fd, &event);
     516  		if (rc < 0)
     517  			break;
     518  		if (rc == 1 && errno == EAGAIN) {
     519  			rc = 0;		/* done */
     520  			break;
     521  		}
     522  		if (rc == 0 && event_match(&event, &id))
     523  			fill_table_row(ctrl->tb, &event);
     524  	}
     525  	close(fd);
     526  	return rc;
     527  }
     528  
     529  static void rfkill_list_output(struct control const *ctrl)
     530  {
     531  	scols_print_table(ctrl->tb);
     532  	scols_unref_table(ctrl->tb);
     533  }
     534  
     535  static int __rfkill_block(int fd, struct rfkill_id *id, uint8_t block, const char *param)
     536  {
     537  	struct rfkill_event event = {
     538  		.op = RFKILL_OP_CHANGE_ALL,
     539  		.soft = block,
     540  		0
     541  	};
     542  	char *message = NULL;
     543  
     544  	switch (id->result) {
     545  	case RFKILL_IS_INVALID:
     546  		warnx(_("invalid identifier: %s"), param);
     547  		return -1;
     548  	case RFKILL_IS_TYPE:
     549  		event.type = id->type;
     550  		xasprintf(&message, "type %s", param);
     551  		break;
     552  	case RFKILL_IS_INDEX:
     553  		event.op = RFKILL_OP_CHANGE;
     554  		event.idx = id->index;
     555  		xasprintf(&message, "id %d", id->index);
     556  		break;
     557  	case RFKILL_IS_ALL:
     558  		message = xstrdup("all");
     559  		break;
     560  	default:
     561  		abort();
     562  	}
     563  
     564  	if (write_all(fd, &event, sizeof(event)) != 0)
     565  		warn(_("write failed: %s"), _PATH_DEV_RFKILL);
     566  	else {
     567  		openlog("rfkill", 0, LOG_USER);
     568  		syslog(LOG_NOTICE, "%s set for %s", block ? "block" : "unblock", message);
     569  		closelog();
     570  	}
     571  	free(message);
     572  	return 0;
     573  }
     574  
     575  static int rfkill_block(uint8_t block, const char *param)
     576  {
     577  	struct rfkill_id id;
     578  	int fd;
     579  
     580  	id = rfkill_id_to_type(param);
     581  	if (id.result == RFKILL_IS_INVALID) {
     582  		warnx(_("invalid identifier: %s"), param);
     583  		return -EINVAL;
     584  	}
     585  
     586  	fd = rfkill_open(0, 0);
     587  	if (fd < 0)
     588  		return fd;
     589  
     590  	__rfkill_block(fd, &id, block, param);
     591  
     592  	return close(fd);
     593  }
     594  
     595  static int rfkill_toggle(const char *param)
     596  {
     597  	struct rfkill_id id = { .result = RFKILL_IS_ALL };
     598  	struct rfkill_event event;
     599  	int fd, rc = 0;
     600  
     601  	id = rfkill_id_to_type(param);
     602  	if (id.result == RFKILL_IS_INVALID) {
     603  		warnx(_("invalid identifier: %s"), param);
     604  		return -EINVAL;
     605  	}
     606  
     607  	fd = rfkill_open(0, 1);
     608  	if (fd < 0)
     609  		return fd;
     610  
     611  	while (1) {
     612  		rc = rfkill_read_event(fd, &event);
     613  		if (rc < 0)
     614  			break;
     615  		if (rc == 1 && errno == EAGAIN) {
     616  			rc = 0;		/* done */
     617  			break;
     618  		}
     619  		if (rc == 0 && event_match(&event, &id))
     620  			__rfkill_block(fd, &id, event.soft ? 0 : 1, param);
     621  	}
     622  
     623  	close(fd);
     624  	return rc;
     625  }
     626  
     627  
     628  static void __attribute__((__noreturn__)) usage(void)
     629  {
     630  	size_t i;
     631  
     632  	fputs(USAGE_HEADER, stdout);
     633  	fprintf(stdout, _(" %s [options] command [identifier ...]\n"), program_invocation_short_name);
     634  
     635  	fputs(USAGE_SEPARATOR, stdout);
     636  	fputs(_("Tool for enabling and disabling wireless devices.\n"), stdout);
     637  
     638  	fputs(USAGE_OPTIONS, stdout);
     639  	fputs(_(" -J, --json             use JSON output format\n"), stdout);
     640  	fputs(_(" -n, --noheadings       don't print headings\n"), stdout);
     641  	fputs(_(" -o, --output <list>    define which output columns to use\n"), stdout);
     642  	fputs(_("     --output-all       output all columns\n"), stdout);
     643  	fputs(_(" -r, --raw              use the raw output format\n"), stdout);
     644  
     645  	fputs(USAGE_SEPARATOR, stdout);
     646  	printf(USAGE_HELP_OPTIONS(24));
     647  
     648  	fputs(USAGE_COLUMNS, stdout);
     649  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     650  		fprintf(stdout, " %-10s  %s\n", infos[i].name, _(infos[i].help));
     651  
     652  	fputs(USAGE_COMMANDS, stdout);
     653  
     654  	/*
     655  	 * TRANSLATORS: command names should not be translated, explaining
     656  	 * them as additional field after identifier is fine, for example
     657  	 *
     658  	 * list   [identifier]   (lista [tarkenne])
     659  	 */
     660  	fputs(_(" help\n"), stdout);
     661  	fputs(_(" event\n"), stdout);
     662  	fputs(_(" list   [identifier]\n"), stdout);
     663  	fputs(_(" block   identifier\n"), stdout);
     664  	fputs(_(" unblock identifier\n"), stdout);
     665  	fputs(_(" toggle  identifier\n"), stdout);
     666  
     667  	fprintf(stdout, USAGE_MAN_TAIL("rfkill(8)"));
     668  	exit(EXIT_SUCCESS);
     669  }
     670  
     671  int main(int argc, char **argv)
     672  {
     673  	struct control ctrl = { 0 };
     674  	int c, act = ACT_LIST, list_all = 0;
     675  	char *outarg = NULL;
     676  	enum {
     677  		OPT_LIST_TYPES = CHAR_MAX + 1
     678  	};
     679  	static const struct option longopts[] = {
     680  		{ "json",	no_argument,	   NULL, 'J' },
     681  		{ "noheadings", no_argument,	   NULL, 'n' },
     682  		{ "output",	required_argument, NULL, 'o' },
     683  		{ "output-all",	no_argument,	   NULL, OPT_LIST_TYPES },
     684  		{ "raw",	no_argument,	   NULL, 'r' },
     685  		{ "version",	no_argument,	   NULL, 'V' },
     686  		{ "help",	no_argument,	   NULL, 'h' },
     687  		{ NULL, 0, NULL, 0 }
     688  	};
     689  	static const ul_excl_t excl[] = {
     690  		{'J', 'r'},
     691  		{0}
     692  	};
     693  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     694  	int ret = 0;
     695  
     696  	setlocale(LC_ALL, "");
     697  	bindtextdomain(PACKAGE, LOCALEDIR);
     698  	textdomain(PACKAGE);
     699  	close_stdout_atexit();
     700  
     701  	while ((c = getopt_long(argc, argv, "Jno:rVh", longopts, NULL)) != -1) {
     702  		err_exclusive_options(c, longopts, excl, excl_st);
     703  		switch (c) {
     704  		case 'J':
     705  			ctrl.json = 1;
     706  			break;
     707  		case 'n':
     708  			ctrl.no_headings = 1;
     709  			break;
     710  		case 'o':
     711  			outarg = optarg;
     712  			break;
     713  		case OPT_LIST_TYPES:
     714  			list_all = 1;
     715  			break;
     716  		case 'r':
     717  			ctrl.raw = 1;
     718  			break;
     719  
     720  		case 'V':
     721  			print_version(EXIT_SUCCESS);
     722  		case 'h':
     723  			usage();
     724  		default:
     725  			errtryhelp(EXIT_FAILURE);
     726  		}
     727  	}
     728  	argc -= optind;
     729  	argv += optind;
     730  
     731  	if (argc > 0) {
     732  		act = string_to_action(*argv);
     733  		if (act < 0)
     734  			errtryhelp(EXIT_FAILURE);
     735  		argv++;
     736  		argc--;
     737  
     738  		/*
     739  		 * For backward compatibility we use old output format if
     740  		 * "list" explicitly specified and --output not defined.
     741  		 */
     742  		if (!outarg && act == ACT_LIST)
     743  			act = ACT_LIST_OLD;
     744  	}
     745  
     746  	switch (act) {
     747  	case ACT_LIST_OLD:
     748  		/* Deprecated in favour of ACT_LIST */
     749  		if (!argc)
     750  			ret |= rfkill_list_old(NULL);	/* ALL */
     751  		else while (argc) {
     752  			ret |= rfkill_list_old(*argv);
     753  			argc--;
     754  			argv++;
     755  		}
     756  		break;
     757  
     758  	case ACT_LIST:
     759  		columns[ncolumns++] = COL_ID;
     760  		columns[ncolumns++] = COL_TYPE;
     761  		columns[ncolumns++] = COL_DEVICE;
     762  		if (list_all)
     763  			columns[ncolumns++] = COL_DESC;
     764  		columns[ncolumns++] = COL_SOFT;
     765  		columns[ncolumns++] = COL_HARD;
     766  
     767  		if (outarg
     768  		    && string_add_to_idarray(outarg, columns,
     769  					     ARRAY_SIZE(columns), &ncolumns,
     770  					     column_name_to_id) < 0)
     771  			return EXIT_FAILURE;
     772  
     773  		rfkill_list_init(&ctrl);
     774  		if (!argc)
     775  			ret |= rfkill_list_fill(&ctrl, NULL);	/* ALL */
     776  		else while (argc) {
     777  			ret |= rfkill_list_fill(&ctrl, *argv);
     778  			argc--;
     779  			argv++;
     780  		}
     781  		rfkill_list_output(&ctrl);
     782  		break;
     783  
     784  	case ACT_EVENT:
     785  		ret = rfkill_event();
     786  		break;
     787  
     788  	case ACT_HELP:
     789  		usage();
     790  		break;
     791  
     792  	case ACT_BLOCK:
     793  		while (argc) {
     794  			ret |= rfkill_block(1, *argv);
     795  			argc--;
     796  			argv++;
     797  		}
     798  		break;
     799  
     800  	case ACT_UNBLOCK:
     801  		while (argc) {
     802  			ret |= rfkill_block(0, *argv);
     803  			argv++;
     804  			argc--;
     805  		}
     806  		break;
     807  
     808  	case ACT_TOGGLE:
     809  		while (argc) {
     810  			ret |= rfkill_toggle(*argv);
     811  			argv++;
     812  			argc--;
     813  		}
     814  		break;
     815  	}
     816  
     817  	return ret ? EXIT_FAILURE : EXIT_SUCCESS;
     818  }