]> git.ipfire.org Git - thirdparty/iw.git/blobdiff - iw.c
split out event handling code
[thirdparty/iw.git] / iw.c
diff --git a/iw.c b/iw.c
index c2b53ee236fc3244eb65cdc587b26fe37fd181e5..6cc82e886b0f5394a795c287e3d8170b3ecda731 100644 (file)
--- a/iw.c
+++ b/iw.c
@@ -1,41 +1,71 @@
 /*
  * nl80211 userspace tool
  *
- * Copyright 2007      Johannes Berg <johannes@sipsolutions.net>
- *
- * GPLv2
+ * Copyright 2007, 2008        Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <errno.h>
 #include <stdio.h>
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdbool.h>
+                     
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>  
 #include <netlink/msg.h>
 #include <netlink/attr.h>
-#include <linux/nl80211.h>
 
+#include "nl80211.h"
 #include "iw.h"
 
+#ifndef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+
+static inline struct nl_handle *nl_socket_alloc(void)
+{
+       return nl_handle_alloc();
+}
+
+static inline void nl_socket_free(struct nl_sock *h)
+{
+       nl_handle_destroy(h);
+}
+
+static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache)
+{
+       struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
+       if (!tmp)
+               return -ENOMEM;
+       *cache = tmp;
+       return 0;
+}
+#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
+#endif /* CONFIG_LIBNL20 */
+
+int iw_debug = 0;
 
 static int nl80211_init(struct nl80211_state *state)
 {
        int err;
 
-       state->nl_handle = nl_handle_alloc();
-       if (!state->nl_handle) {
-               fprintf(stderr, "Failed to allocate netlink handle.\n");
+       state->nl_sock = nl_socket_alloc();
+       if (!state->nl_sock) {
+               fprintf(stderr, "Failed to allocate netlink socket.\n");
                return -ENOMEM;
        }
 
-       if (genl_connect(state->nl_handle)) {
+       if (genl_connect(state->nl_sock)) {
                fprintf(stderr, "Failed to connect to generic netlink.\n");
                err = -ENOLINK;
                goto out_handle_destroy;
        }
 
-       state->nl_cache = genl_ctrl_alloc_cache(state->nl_handle);
-       if (!state->nl_cache) {
+       if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) {
                fprintf(stderr, "Failed to allocate generic netlink cache.\n");
                err = -ENOMEM;
                goto out_handle_destroy;
@@ -53,7 +83,7 @@ static int nl80211_init(struct nl80211_state *state)
  out_cache_free:
        nl_cache_free(state->nl_cache);
  out_handle_destroy:
-       nl_handle_destroy(state->nl_handle);
+       nl_socket_free(state->nl_sock);
        return err;
 }
 
@@ -61,88 +91,297 @@ static void nl80211_cleanup(struct nl80211_state *state)
 {
        genl_family_put(state->nl80211);
        nl_cache_free(state->nl_cache);
-       nl_handle_destroy(state->nl_handle);
+       nl_socket_free(state->nl_sock);
 }
 
-/*
- * return
- *     0 - error
- *     1 - phy
- *     2 - dev
- */
-static int get_phy_or_dev(int *argc, char ***argv, char **name)
+__COMMAND(NULL, NULL, "", NULL, 0, 0, 0, CIB_NONE, NULL);
+__COMMAND(NULL, NULL, "", NULL, 1, 0, 0, CIB_NONE, NULL);
+
+static int cmd_size;
+
+static void usage_cmd(struct cmd *cmd)
 {
-       char *type = (*argv)[0];
+       switch (cmd->idby) {
+       case CIB_NONE:
+               fprintf(stderr, "\t");
+               break;
+       case CIB_PHY:
+               fprintf(stderr, "\tphy <phyname> ");
+               break;
+       case CIB_NETDEV:
+               fprintf(stderr, "\tdev <devname> ");
+               break;
+       }
+       if (cmd->section)
+               fprintf(stderr, "%s ", cmd->section);
+       fprintf(stderr, "%s", cmd->name);
+       if (cmd->args)
+               fprintf(stderr, " %s", cmd->args);
+       fprintf(stderr, "\n");
+}
 
-       if (*argc < 2)
-               return 0;
+static void usage(const char *argv0)
+{
+       struct cmd *cmd;
 
-       *name = (*argv)[1];
+       fprintf(stderr, "Usage:\t%s [options] command\n", argv0);
+       fprintf(stderr, "Options:\n");
+       fprintf(stderr, "\t--debug\t\tenable netlink debugging\n");
+       fprintf(stderr, "\t--version\tshow version\n");
+       fprintf(stderr, "Commands:\n");
+       fprintf(stderr, "\thelp\n");
+       for (cmd = &__start___cmd; cmd < &__stop___cmd;
+            cmd = (struct cmd *)((char *)cmd + cmd_size)) {
+               if (!cmd->handler || cmd->hidden)
+                       continue;
+               usage_cmd(cmd);
+       }
+}
 
-       *argc -= 2;
-       *argv += 2;
+static void version(void)
+{
+       printf("iw version %s\n", iw_version);
+}
 
-       if (strcmp(type, "phy") == 0)
-               return 1;
-       if (strcmp(type, "dev") == 0)
-               return 2;
+static int phy_lookup(char *name)
+{
+       char buf[200];
+       int fd, pos;
 
-       return 0;
+       snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
+
+       fd = open(buf, O_RDONLY);
+       if (fd < 0)
+               return -1;
+       pos = read(fd, buf, sizeof(buf) - 1);
+       if (pos < 0)
+               return -1;
+       buf[pos] = '\0';
+       return atoi(buf);
 }
 
-void usage(char *argv0)
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+                        void *arg)
 {
-       fprintf(stderr, "Usage: %s [options] {dev <phydev>} {interface <interface> } {COMMAND}\n"
-                       "where COMMAND := { add | del }\n", argv0);
+       int *ret = arg;
+       *ret = err->error;
+       return NL_STOP;
 }
 
-int main(int argc, char **argv)
+static int finish_handler(struct nl_msg *msg, void *arg)
 {
-       struct nl80211_state nlstate;
-       int err = 0, pod;
-       char *ifname = NULL, *phyname = NULL, *type, *argv0;
+       int *ret = arg;
+       *ret = 0;
+       return NL_SKIP;
+}
 
-       err = nl80211_init(&nlstate);
-       if (err)
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_STOP;
+}
+
+int handle_cmd(struct nl80211_state *state, enum id_input idby,
+              int argc, char **argv)
+{
+       struct cmd *cmd, *match = NULL;
+       struct nl_cb *cb;
+       struct nl_msg *msg;
+       int devidx = 0;
+       int err, o_argc;
+       const char *command, *section;
+       char *tmp, **o_argv;
+       enum command_identify_by command_idby = CIB_NONE;
+
+       if (argc <= 1 && idby != II_NONE)
                return 1;
 
-       /* strip off self */
+       o_argc = argc;
+       o_argv = argv;
+
+       switch (idby) {
+       case II_PHY_IDX:
+               command_idby = CIB_PHY;
+               devidx = strtoul(*argv + 4, &tmp, 0);
+               if (*tmp != '\0')
+                       return 1;
+               argc--;
+               argv++;
+               break;
+       case II_PHY_NAME:
+               command_idby = CIB_PHY;
+               devidx = phy_lookup(*argv);
+               argc--;
+               argv++;
+               break;
+       case II_NETDEV:
+               command_idby = CIB_NETDEV;
+               devidx = if_nametoindex(*argv);
+               if (devidx == 0)
+                       devidx = -1;
+               argc--;
+               argv++;
+               break;
+       default:
+               break;
+       }
+
+       if (devidx < 0)
+               return -errno;
+
+       section = command = *argv;
        argc--;
-       argv0 = *argv++;
+       argv++;
 
-       if (argc == 0 || (argc == 1 && strcmp(*argv, "help") == 0)) {
-               usage(argv0);
-               goto out;
+       for (cmd = &__start___cmd; cmd < &__stop___cmd;
+            cmd = (struct cmd *)((char *)cmd + cmd_size)) {
+               if (!cmd->handler)
+                       continue;
+               if (cmd->idby != command_idby)
+                       continue;
+               if (cmd->section) {
+                       if (strcmp(cmd->section, section))
+                               continue;
+                       /* this is a bit icky ... */
+                       if (command == section) {
+                               if (argc <= 0) {
+                                       if (match)
+                                               break;
+                                       return 1;
+                               }
+                               command = *argv;
+                               argc--;
+                               argv++;
+                       }
+               } else if (section != command)
+                       continue;
+               if (strcmp(cmd->name, command))
+                       continue;
+               if (argc && !cmd->args)
+                       continue;
+
+               match = cmd;
        }
 
-       pod = get_phy_or_dev(&argc, &argv, &ifname);
-       if (pod == 0) {
-               err = 1;
-               goto out;
+       cmd = match;
+
+       if (!cmd)
+               return 1;
+
+       if (!cmd->cmd) {
+               argc = o_argc;
+               argv = o_argv;
+               return cmd->handler(state, NULL, NULL, argc, argv);
        }
 
-       if (pod == 1) {
-               phyname = ifname;
-               ifname = NULL;
+       msg = nlmsg_alloc();
+       if (!msg) {
+               fprintf(stderr, "failed to allocate netlink message\n");
+               return 2;
        }
 
-       if (argc <= 0) {
-               err = 1;
-               goto out;
+       cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT);
+       if (!cb) {
+               fprintf(stderr, "failed to allocate netlink callbacks\n");
+               err = 2;
+               goto out_free_msg;
        }
 
-       type = argv[0];
-       argc--;
-       argv++;
+       genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
+                   cmd->nl_msg_flags, cmd->cmd, 0);
 
-       if (strcmp(type, "interface") == 0)
-               err = handle_interface(&nlstate, phyname, ifname, argc, argv);
-       else {
-               fprintf(stderr, "No such object type %s\n", type);
-               err = 1;
+       switch (command_idby) {
+       case CIB_PHY:
+               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
+               break;
+       case CIB_NETDEV:
+               NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx);
+               break;
+       default:
+               break;
        }
 
+       err = cmd->handler(state, cb, msg, argc, argv);
+       if (err)
+               goto out;
+
+       err = nl_send_auto_complete(state->nl_sock, msg);
+       if (err < 0)
+               goto out;
+
+       err = 1;
+
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+       while (err > 0)
+               nl_recvmsgs(state->nl_sock, cb);
  out:
+       nl_cb_put(cb);
+ out_free_msg:
+       nlmsg_free(msg);
+       return err;
+ nla_put_failure:
+       fprintf(stderr, "building message failed\n");
+       return 2;
+}
+
+int main(int argc, char **argv)
+{
+       struct nl80211_state nlstate;
+       int err;
+       const char *argv0;
+
+       /* calculate command size including padding */
+       cmd_size = abs((long)&__cmd_NULL_NULL_1_CIB_NONE_0
+                    - (long)&__cmd_NULL_NULL_0_CIB_NONE_0);
+       /* strip off self */
+       argc--;
+       argv0 = *argv++;
+
+       if (argc > 0 && strcmp(*argv, "--debug") == 0) {
+               iw_debug = 1;
+               argc--;
+               argv++;
+       }
+
+       if (argc > 0 && strcmp(*argv, "--version") == 0) {
+               version();
+               return 0;
+       }
+
+       if (argc == 0 || strcmp(*argv, "help") == 0) {
+               usage(argv0);
+               return 0;
+       }
+
+       err = nl80211_init(&nlstate);
+       if (err)
+               return 1;
+
+       if (strcmp(*argv, "dev") == 0 && argc > 1) {
+               argc--;
+               argv++;
+               err = handle_cmd(&nlstate, II_NETDEV, argc, argv);
+       } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) {
+               if (strlen(*argv) == 3) {
+                       argc--;
+                       argv++;
+                       err = handle_cmd(&nlstate, II_PHY_NAME, argc, argv);
+               } else if (*(*argv + 3) == '#')
+                       err = handle_cmd(&nlstate, II_PHY_IDX, argc, argv);
+               else
+                       err = 1;
+       } else
+               err = handle_cmd(&nlstate, II_NONE, argc, argv);
+
+       if (err == 1)
+               usage(argv0);
+       if (err < 0)
+               fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err);
+
        nl80211_cleanup(&nlstate);
 
        return err;