root/drivers/s390/net/smsgiucv_app.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. smsg_app_event_free
  2. smsg_app_event_alloc
  3. smsg_event_work_fn
  4. smsg_app_callback
  5. smsgiucv_app_init
  6. smsgiucv_app_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Deliver z/VM CP special messages (SMSG) as uevents.
   4  *
   5  * The driver registers for z/VM CP special messages with the
   6  * "APP" prefix. Incoming messages are delivered to user space
   7  * as uevents.
   8  *
   9  * Copyright IBM Corp. 2010
  10  * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
  11  *
  12  */
  13 #define KMSG_COMPONENT          "smsgiucv_app"
  14 #define pr_fmt(fmt)             KMSG_COMPONENT ": " fmt
  15 
  16 #include <linux/ctype.h>
  17 #include <linux/err.h>
  18 #include <linux/device.h>
  19 #include <linux/list.h>
  20 #include <linux/kobject.h>
  21 #include <linux/module.h>
  22 #include <linux/slab.h>
  23 #include <linux/spinlock.h>
  24 #include <linux/workqueue.h>
  25 #include <net/iucv/iucv.h>
  26 #include "smsgiucv.h"
  27 
  28 /* prefix used for SMSG registration */
  29 #define SMSG_PREFIX             "APP"
  30 
  31 /* SMSG related uevent environment variables */
  32 #define ENV_SENDER_STR          "SMSG_SENDER="
  33 #define ENV_SENDER_LEN          (strlen(ENV_SENDER_STR) + 8 + 1)
  34 #define ENV_PREFIX_STR          "SMSG_ID="
  35 #define ENV_PREFIX_LEN          (strlen(ENV_PREFIX_STR) + \
  36                                  strlen(SMSG_PREFIX) + 1)
  37 #define ENV_TEXT_STR            "SMSG_TEXT="
  38 #define ENV_TEXT_LEN(msg)       (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
  39 
  40 /* z/VM user ID which is permitted to send SMSGs
  41  * If the value is undefined or empty (""), special messages are
  42  * accepted from any z/VM user ID. */
  43 static char *sender;
  44 module_param(sender, charp, 0400);
  45 MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
  46 
  47 /* SMSG device representation */
  48 static struct device *smsg_app_dev;
  49 
  50 /* list element for queuing received messages for delivery */
  51 struct smsg_app_event {
  52         struct list_head list;
  53         char *buf;
  54         char *envp[4];
  55 };
  56 
  57 /* queue for outgoing uevents */
  58 static LIST_HEAD(smsg_event_queue);
  59 static DEFINE_SPINLOCK(smsg_event_queue_lock);
  60 
  61 static void smsg_app_event_free(struct smsg_app_event *ev)
  62 {
  63         kfree(ev->buf);
  64         kfree(ev);
  65 }
  66 
  67 static struct smsg_app_event *smsg_app_event_alloc(const char *from,
  68                                                    const char *msg)
  69 {
  70         struct smsg_app_event *ev;
  71 
  72         ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
  73         if (!ev)
  74                 return NULL;
  75 
  76         ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
  77                           ENV_TEXT_LEN(msg), GFP_ATOMIC);
  78         if (!ev->buf) {
  79                 kfree(ev);
  80                 return NULL;
  81         }
  82 
  83         /* setting up environment pointers into buf */
  84         ev->envp[0] = ev->buf;
  85         ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
  86         ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
  87         ev->envp[3] = NULL;
  88 
  89         /* setting up environment: sender, prefix name, and message text */
  90         snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
  91         snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
  92         snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
  93 
  94         return ev;
  95 }
  96 
  97 static void smsg_event_work_fn(struct work_struct *work)
  98 {
  99         LIST_HEAD(event_queue);
 100         struct smsg_app_event *p, *n;
 101         struct device *dev;
 102 
 103         dev = get_device(smsg_app_dev);
 104         if (!dev)
 105                 return;
 106 
 107         spin_lock_bh(&smsg_event_queue_lock);
 108         list_splice_init(&smsg_event_queue, &event_queue);
 109         spin_unlock_bh(&smsg_event_queue_lock);
 110 
 111         list_for_each_entry_safe(p, n, &event_queue, list) {
 112                 list_del(&p->list);
 113                 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
 114                 smsg_app_event_free(p);
 115         }
 116 
 117         put_device(dev);
 118 }
 119 static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
 120 
 121 static void smsg_app_callback(const char *from, char *msg)
 122 {
 123         struct smsg_app_event *se;
 124 
 125         /* check if the originating z/VM user ID matches
 126          * the configured sender. */
 127         if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
 128                 return;
 129 
 130         /* get start of message text (skip prefix and leading blanks) */
 131         msg += strlen(SMSG_PREFIX);
 132         while (*msg && isspace(*msg))
 133                 msg++;
 134         if (*msg == '\0')
 135                 return;
 136 
 137         /* allocate event list element and its environment */
 138         se = smsg_app_event_alloc(from, msg);
 139         if (!se)
 140                 return;
 141 
 142         /* queue event and schedule work function */
 143         spin_lock(&smsg_event_queue_lock);
 144         list_add_tail(&se->list, &smsg_event_queue);
 145         spin_unlock(&smsg_event_queue_lock);
 146 
 147         schedule_work(&smsg_event_work);
 148         return;
 149 }
 150 
 151 static int __init smsgiucv_app_init(void)
 152 {
 153         struct device_driver *smsgiucv_drv;
 154         int rc;
 155 
 156         if (!MACHINE_IS_VM)
 157                 return -ENODEV;
 158 
 159         smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
 160         if (!smsg_app_dev)
 161                 return -ENOMEM;
 162 
 163         smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
 164         if (!smsgiucv_drv) {
 165                 kfree(smsg_app_dev);
 166                 return -ENODEV;
 167         }
 168 
 169         rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
 170         if (rc) {
 171                 kfree(smsg_app_dev);
 172                 goto fail;
 173         }
 174         smsg_app_dev->bus = &iucv_bus;
 175         smsg_app_dev->parent = iucv_root;
 176         smsg_app_dev->release = (void (*)(struct device *)) kfree;
 177         smsg_app_dev->driver = smsgiucv_drv;
 178         rc = device_register(smsg_app_dev);
 179         if (rc) {
 180                 put_device(smsg_app_dev);
 181                 goto fail;
 182         }
 183 
 184         /* convert sender to uppercase characters */
 185         if (sender) {
 186                 int len = strlen(sender);
 187                 while (len--)
 188                         sender[len] = toupper(sender[len]);
 189         }
 190 
 191         /* register with the smsgiucv device driver */
 192         rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
 193         if (rc) {
 194                 device_unregister(smsg_app_dev);
 195                 goto fail;
 196         }
 197 
 198         rc = 0;
 199 fail:
 200         return rc;
 201 }
 202 module_init(smsgiucv_app_init);
 203 
 204 static void __exit smsgiucv_app_exit(void)
 205 {
 206         /* unregister callback */
 207         smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
 208 
 209         /* cancel pending work and flush any queued event work */
 210         cancel_work_sync(&smsg_event_work);
 211         smsg_event_work_fn(&smsg_event_work);
 212 
 213         device_unregister(smsg_app_dev);
 214 }
 215 module_exit(smsgiucv_app_exit);
 216 
 217 MODULE_LICENSE("GPL v2");
 218 MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
 219 MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");

/* [<][>][^][v][top][bottom][index][help] */