This commit was generated by cvs2svn to compensate for changes in r786,
[libnl.git] / lib / handlers.c
diff --git a/lib/handlers.c b/lib/handlers.c
new file mode 100644 (file)
index 0000000..c433230
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * lib/handlers.c      default netlink message handlers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup nl
+ * @defgroup cb Callbacks/Customization
+ * 
+ * Customization via callbacks.
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/msg.h>
+#include <netlink/handlers.h>
+
+static void print_header_content(FILE *ofd, struct nlmsghdr *n)
+{
+       char flags[128];
+       char type[32];
+       
+       fprintf(ofd, "type=%s length=%u flags=<%s> sequence-nr=%u pid=%u",
+               nl_nlmsgtype2str(n->nlmsg_type, type, sizeof(type)),
+               n->nlmsg_len, nl_nlmsg_flags2str(n->nlmsg_flags, flags,
+               sizeof(flags)), n->nlmsg_seq, n->nlmsg_pid);
+}
+
+static inline void dump_hex(FILE *ofd, char *start, int len)
+{
+       int i, a, c, limit;
+       char ascii[21] = {0};
+
+       limit = 18;
+       fprintf(ofd, "    ");
+
+       for (i = 0, a = 0, c = 0; i < len; i++) {
+               int v = *(uint8_t *) (start + i);
+
+               fprintf(ofd, "%02x ", v);
+               ascii[a++] = isprint(v) ? v : '.';
+
+               if (c == limit-1) {
+                       fprintf(ofd, "%s\n", ascii);
+                       if (i < (len - 1))
+                               fprintf(ofd, "    ");
+                       a = c = 0;
+                       memset(ascii, 0, sizeof(ascii));
+               } else
+                       c++;
+       }
+
+       if (c != 0) {
+               for (i = 0; i < (limit - c); i++)
+                       fprintf(ofd, "   ");
+               fprintf(ofd, "%s\n", ascii);
+       }
+}
+
+static void print_hdr(FILE *ofd, struct nl_msg *msg)
+{
+       struct nlmsghdr *nlh = nlmsg_hdr(msg);
+       struct nl_cache_ops *ops;
+       char buf[128];
+
+       fprintf(ofd, "    .nlmsg_len = %d\n", nlh->nlmsg_len);
+
+       ops = nl_cache_mngt_associate(nlmsg_get_proto(msg), nlh->nlmsg_type);
+
+       fprintf(ofd, "    .nlmsg_type = %d <%s>\n", nlh->nlmsg_type,
+               ops ? nl_cache_mngt_type2name(ops, nlh->nlmsg_type,
+                                             buf, sizeof(buf))
+               : nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf)));
+       fprintf(ofd, "    .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags,
+               nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf)));
+       fprintf(ofd, "    .nlmsg_seq = %d\n", nlh->nlmsg_seq);
+       fprintf(ofd, "    .nlmsg_pid = %d\n", nlh->nlmsg_pid);
+
+}
+
+static void raw_dump_msg(FILE *ofd, struct nl_msg *msg)
+{
+       struct nlmsghdr *hdr = nlmsg_hdr(msg);
+       
+       fprintf(ofd, 
+       "--------------------------   BEGIN NETLINK MESSAGE "
+       "---------------------------\n");
+
+       fprintf(ofd, "  [HEADER] %Zu octets\n", sizeof(struct nlmsghdr));
+       print_hdr(ofd, msg);
+
+       if (hdr->nlmsg_type == NLMSG_ERROR &&
+           hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) {
+               struct nl_msg *errmsg;
+               struct nlmsgerr *err = nlmsg_data(hdr);
+
+               fprintf(ofd, "  [ERRORMSG] %Zu octets\n", sizeof(*err));
+               fprintf(ofd, "    .error = %d \"%s\"\n", err->error,
+                       strerror(-err->error));
+               fprintf(ofd, "  [ORIGINAL MESSAGE] %Zu octets\n", sizeof(*hdr));
+
+               errmsg = nlmsg_build(&err->msg);
+               print_hdr(ofd, errmsg);
+               nlmsg_free(msg);
+       } else if (nlmsg_len(hdr) > 0) {
+               struct nl_cache_ops *ops;
+               int payloadlen = nlmsg_len(hdr);
+               int attrlen = 0;
+
+               ops = nl_cache_mngt_associate(nlmsg_get_proto(msg),
+                                             hdr->nlmsg_type);
+               if (ops) {
+                       attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize);
+                       payloadlen -= attrlen;
+               }
+
+               fprintf(ofd, "  [PAYLOAD] %d octets\n", payloadlen);
+               dump_hex(ofd, nlmsg_data(hdr), payloadlen);
+
+               if (attrlen) {
+                       int rem, padlen;
+                       struct nlattr *nla;
+       
+                       nlmsg_for_each_attr(nla, hdr, ops->co_hdrsize, rem) {
+                               int alen = nla_len(nla);
+
+                               fprintf(ofd, "  [ATTR %02d] %d octets\n",
+                                       nla->nla_type, alen);
+                               dump_hex(ofd, nla_data(nla), alen);
+
+                               padlen = nla_padlen(alen);
+                               if (padlen > 0) {
+                                       fprintf(ofd, "  [PADDING] %d octets\n",
+                                               padlen);
+                                       dump_hex(ofd, nla_data(nla) + alen,
+                                                padlen);
+                               }
+                       }
+               }
+       }
+
+       fprintf(ofd, 
+       "---------------------------  END NETLINK MESSAGE   "
+       "---------------------------\n");
+}
+
+static int nl_valid_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_PROCEED;
+}
+
+static int nl_finish_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_invalid_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_msg_in_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_PROCEED;
+}
+
+static int nl_msg_out_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_PROCEED;
+}
+
+static int nl_overrun_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_skipped_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_SKIP;
+}
+
+static int nl_ack_handler_default(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_error_handler_default(struct sockaddr_nl *who,
+                                   struct nlmsgerr *e, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_valid_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stdout;
+
+       fprintf(ofd, "-- Warning: unhandled valid message: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+
+       return NL_PROCEED;
+}
+
+static int nl_finish_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_msg_in_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       return NL_PROCEED;
+}
+
+static int nl_invalid_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Error: Invalid message: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+
+       return NL_EXIT;
+}
+
+static int nl_msg_out_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       return NL_PROCEED;
+}
+
+static int nl_overrun_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Error: Netlink Overrun: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+       
+       return NL_EXIT;
+}
+
+static int nl_ack_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       return NL_EXIT;
+}
+
+static int nl_skipped_handler_verbose(struct nl_msg *msg, void *arg)
+{
+       return NL_SKIP;
+}
+
+static int nl_error_handler_verbose(struct sockaddr_nl *who,
+                                   struct nlmsgerr *e, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Error received: %s\n-- Original message: ",
+               strerror(-e->error));
+       print_header_content(ofd, &e->msg);
+       fprintf(ofd, "\n");
+
+       return NL_EXIT;
+}
+
+static int nl_valid_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: Valid message: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+
+       return NL_PROCEED;
+}
+
+static int nl_finish_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: End of multipart message block: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+       
+       return NL_EXIT;
+}
+
+static int nl_invalid_handler_debug(struct nl_msg *msg, void *arg)
+{
+       return nl_invalid_handler_verbose(msg, arg);
+}
+
+static int nl_msg_in_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: Received Message:\n");
+       raw_dump_msg(ofd, msg);
+       
+       return NL_PROCEED;
+}
+
+static int nl_msg_out_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: Sent Message:\n");
+       raw_dump_msg(ofd, msg);
+
+       return NL_PROCEED;
+}
+
+static int nl_overrun_handler_debug(struct nl_msg *msg, void *arg)
+{
+       return nl_overrun_handler_verbose(msg, arg);
+}
+
+static int nl_skipped_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: Skipped message: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+
+       return NL_SKIP;
+}
+
+static int nl_ack_handler_debug(struct nl_msg *msg, void *arg)
+{
+       FILE *ofd = arg ? arg : stderr;
+
+       fprintf(ofd, "-- Debug: ACK: ");
+       print_header_content(ofd, nlmsg_hdr(msg));
+       fprintf(ofd, "\n");
+
+       return NL_EXIT;
+}
+
+static int nl_error_handler_debug(struct sockaddr_nl *who,
+                                 struct nlmsgerr *e, void *arg)
+{
+       return nl_error_handler_verbose(who, e, arg);
+}
+
+static nl_recvmsg_msg_cb_t cb_def[NL_CB_TYPE_MAX+1][NL_CB_KIND_MAX+1] = {
+       [NL_CB_VALID] = {
+               [NL_CB_DEFAULT] = nl_valid_handler_default,
+               [NL_CB_VERBOSE] = nl_valid_handler_verbose,
+               [NL_CB_DEBUG]   = nl_valid_handler_debug,
+       },
+       [NL_CB_FINISH] = {
+               [NL_CB_DEFAULT] = nl_finish_handler_default,
+               [NL_CB_VERBOSE] = nl_finish_handler_verbose,
+               [NL_CB_DEBUG]   = nl_finish_handler_debug,
+       },
+       [NL_CB_INVALID] = {
+               [NL_CB_DEFAULT] = nl_invalid_handler_default,
+               [NL_CB_VERBOSE] = nl_invalid_handler_verbose,
+               [NL_CB_DEBUG]   = nl_invalid_handler_debug,
+       },
+       [NL_CB_MSG_IN] = {
+               [NL_CB_DEFAULT] = nl_msg_in_handler_default,
+               [NL_CB_VERBOSE] = nl_msg_in_handler_verbose,
+               [NL_CB_DEBUG]   = nl_msg_in_handler_debug,
+       },
+       [NL_CB_MSG_OUT] = {
+               [NL_CB_DEFAULT] = nl_msg_out_handler_default,
+               [NL_CB_VERBOSE] = nl_msg_out_handler_verbose,
+               [NL_CB_DEBUG]   = nl_msg_out_handler_debug,
+       },
+       [NL_CB_OVERRUN] = {
+               [NL_CB_DEFAULT] = nl_overrun_handler_default,
+               [NL_CB_VERBOSE] = nl_overrun_handler_verbose,
+               [NL_CB_DEBUG]   = nl_overrun_handler_debug,
+       },
+       [NL_CB_SKIPPED] = {
+               [NL_CB_DEFAULT] = nl_skipped_handler_default,
+               [NL_CB_VERBOSE] = nl_skipped_handler_verbose,
+               [NL_CB_DEBUG]   = nl_skipped_handler_debug,
+       },
+       [NL_CB_ACK] = {
+               [NL_CB_DEFAULT] = nl_ack_handler_default,
+               [NL_CB_VERBOSE] = nl_ack_handler_verbose,
+               [NL_CB_DEBUG]   = nl_ack_handler_debug,
+       },
+};
+
+static nl_recvmsg_err_cb_t cb_err_def[NL_CB_KIND_MAX+1] = {
+       [NL_CB_DEFAULT] = nl_error_handler_default,
+       [NL_CB_VERBOSE] = nl_error_handler_verbose,
+       [NL_CB_DEBUG]   = nl_error_handler_debug,
+};
+
+/**
+ * @name Callback Handle Management
+ * @{
+ */
+
+/**
+ * Allocate a new callback handle
+ * @arg kind           callback kind to be used for initialization
+ * @return Newly allocated callback handle or NULL
+ */
+struct nl_cb *nl_cb_new(enum nl_cb_kind kind)
+{
+       int i;
+       struct nl_cb *cb;
+
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return NULL;
+
+       cb = calloc(1, sizeof(*cb));
+       if (!cb) {
+               nl_errno(ENOMEM);
+               return NULL;
+       }
+
+       for (i = 0; i <= NL_CB_TYPE_MAX; i++)
+               nl_cb_set(cb, i, kind, NULL, NULL);
+
+       nl_cb_err(cb, kind, NULL, NULL);
+
+       return cb;
+}
+
+/**
+ * Destroy a callback handle
+ * @arg cb             callback handle
+ */
+void nl_cb_destroy(struct nl_cb *cb)
+{
+       free(cb);
+}
+
+/**
+ * Clone an existing callback handle
+ * @arg orig           original callback handle
+ * @return Newly allocated callback handle being a duplicate of
+ *         orig or NULL
+ */
+struct nl_cb *nl_cb_clone(struct nl_cb *orig)
+{
+       struct nl_cb *cb;
+       
+       cb = nl_cb_new(NL_CB_DEFAULT);
+       if (!cb)
+               return NULL;
+
+       memcpy(cb, orig, sizeof(*orig));
+
+       return cb;
+}
+
+/** @} */
+
+/**
+ * @name Callback Setup
+ * @{
+ */
+
+/**
+ * Set up a callback 
+ * @arg cb             callback configuration
+ * @arg type           which type callback to set
+ * @arg kind           kind of callback
+ * @arg func           callback function
+ * @arg arg            argument to be passwd to callback function
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,
+             nl_recvmsg_msg_cb_t func, void *arg)
+{
+       if (type < 0 || type > NL_CB_TYPE_MAX)
+               return nl_error(ERANGE, "Callback type out of range");
+
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return nl_error(ERANGE, "Callback kind out of range");
+
+       if (kind == NL_CB_CUSTOM) {
+               cb->cb_set[type] = func;
+               cb->cb_args[type] = arg;
+       } else {
+               cb->cb_set[type] = cb_def[type][kind];
+               cb->cb_args[type] = arg;
+       }
+
+       return 0;
+}
+
+/**
+ * Set up a all callbacks
+ * @arg cb             callback configuration
+ * @arg kind           kind of callback
+ * @arg func           callback function
+ * @arg arg            argument to be passwd to callback function
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind,
+                 nl_recvmsg_msg_cb_t func, void *arg)
+{
+       int i, err;
+
+       for (i = 0; i <= NL_CB_TYPE_MAX; i++) {
+               err = nl_cb_set(cb, i, kind, func, arg);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/**
+ * Set up an error callback
+ * @arg cb             callback configuration
+ * @arg kind           kind of callback
+ * @arg func           callback function
+ * @arg arg            argument to be passed to callback function
+ */
+int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind,
+             nl_recvmsg_err_cb_t func, void *arg)
+{
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return nl_error(ERANGE, "Callback kind out of range");
+
+       if (kind == NL_CB_CUSTOM) {
+               cb->cb_err = func;
+               cb->cb_err_arg = arg;
+       } else {
+               cb->cb_err = cb_err_def[kind];
+               cb->cb_err_arg = arg;
+       }
+
+       return 0;
+}
+
+/**
+ * Overwrite internal calls to nl_recvmsgs()
+ * @arg cb             callback configuration
+ * @arg func           replacement callback for nl_recvmsgs()
+ */
+void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
+                             int (*func)(struct nl_handle *, struct nl_cb *))
+{
+       cb->cb_recvmsgs_ow = func;
+}
+
+/**
+ * Overwrite internal calls to nl_recv()
+ * @arg cb             callback configuration
+ * @arg func           replacement callback for nl_recv()
+ */
+void nl_cb_overwrite_recv(struct nl_cb *cb,
+                         int (*func)(struct nl_handle *, struct sockaddr_nl *,
+                                     unsigned char **, struct ucred **))
+{
+       cb->cb_recv_ow = func;
+}
+
+/**
+ * Overwrite internal calls to nl_send()
+ * @arg cb             callback configuration
+ * @arg func           replacement callback for nl_send()
+ */
+void nl_cb_overwrite_send(struct nl_cb *cb,
+                         int (*func)(struct nl_handle *, struct nl_msg *))
+{
+       cb->cb_send_ow = func;
+}
+
+/** @} */
+
+/** @} */