0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define KMSG_COMPONENT "smsgiucv_app"
0014 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0015
0016 #include <linux/ctype.h>
0017 #include <linux/err.h>
0018 #include <linux/device.h>
0019 #include <linux/list.h>
0020 #include <linux/kobject.h>
0021 #include <linux/module.h>
0022 #include <linux/slab.h>
0023 #include <linux/spinlock.h>
0024 #include <linux/workqueue.h>
0025 #include <net/iucv/iucv.h>
0026 #include "smsgiucv.h"
0027
0028
0029 #define SMSG_PREFIX "APP"
0030
0031
0032 #define ENV_SENDER_STR "SMSG_SENDER="
0033 #define ENV_SENDER_LEN (strlen(ENV_SENDER_STR) + 8 + 1)
0034 #define ENV_PREFIX_STR "SMSG_ID="
0035 #define ENV_PREFIX_LEN (strlen(ENV_PREFIX_STR) + \
0036 strlen(SMSG_PREFIX) + 1)
0037 #define ENV_TEXT_STR "SMSG_TEXT="
0038 #define ENV_TEXT_LEN(msg) (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
0039
0040
0041
0042
0043 static char *sender;
0044 module_param(sender, charp, 0400);
0045 MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
0046
0047
0048 static struct device *smsg_app_dev;
0049
0050
0051 struct smsg_app_event {
0052 struct list_head list;
0053 char *buf;
0054 char *envp[4];
0055 };
0056
0057
0058 static LIST_HEAD(smsg_event_queue);
0059 static DEFINE_SPINLOCK(smsg_event_queue_lock);
0060
0061 static void smsg_app_event_free(struct smsg_app_event *ev)
0062 {
0063 kfree(ev->buf);
0064 kfree(ev);
0065 }
0066
0067 static struct smsg_app_event *smsg_app_event_alloc(const char *from,
0068 const char *msg)
0069 {
0070 struct smsg_app_event *ev;
0071
0072 ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
0073 if (!ev)
0074 return NULL;
0075
0076 ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
0077 ENV_TEXT_LEN(msg), GFP_ATOMIC);
0078 if (!ev->buf) {
0079 kfree(ev);
0080 return NULL;
0081 }
0082
0083
0084 ev->envp[0] = ev->buf;
0085 ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
0086 ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
0087 ev->envp[3] = NULL;
0088
0089
0090 snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
0091 snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
0092 snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
0093
0094 return ev;
0095 }
0096
0097 static void smsg_event_work_fn(struct work_struct *work)
0098 {
0099 LIST_HEAD(event_queue);
0100 struct smsg_app_event *p, *n;
0101 struct device *dev;
0102
0103 dev = get_device(smsg_app_dev);
0104 if (!dev)
0105 return;
0106
0107 spin_lock_bh(&smsg_event_queue_lock);
0108 list_splice_init(&smsg_event_queue, &event_queue);
0109 spin_unlock_bh(&smsg_event_queue_lock);
0110
0111 list_for_each_entry_safe(p, n, &event_queue, list) {
0112 list_del(&p->list);
0113 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
0114 smsg_app_event_free(p);
0115 }
0116
0117 put_device(dev);
0118 }
0119 static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
0120
0121 static void smsg_app_callback(const char *from, char *msg)
0122 {
0123 struct smsg_app_event *se;
0124
0125
0126
0127 if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
0128 return;
0129
0130
0131 msg += strlen(SMSG_PREFIX);
0132 while (*msg && isspace(*msg))
0133 msg++;
0134 if (*msg == '\0')
0135 return;
0136
0137
0138 se = smsg_app_event_alloc(from, msg);
0139 if (!se)
0140 return;
0141
0142
0143 spin_lock(&smsg_event_queue_lock);
0144 list_add_tail(&se->list, &smsg_event_queue);
0145 spin_unlock(&smsg_event_queue_lock);
0146
0147 schedule_work(&smsg_event_work);
0148 return;
0149 }
0150
0151 static int __init smsgiucv_app_init(void)
0152 {
0153 struct device_driver *smsgiucv_drv;
0154 int rc;
0155
0156 if (!MACHINE_IS_VM)
0157 return -ENODEV;
0158
0159 smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
0160 if (!smsg_app_dev)
0161 return -ENOMEM;
0162
0163 smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
0164 if (!smsgiucv_drv) {
0165 kfree(smsg_app_dev);
0166 return -ENODEV;
0167 }
0168
0169 rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
0170 if (rc) {
0171 kfree(smsg_app_dev);
0172 goto fail;
0173 }
0174 smsg_app_dev->bus = &iucv_bus;
0175 smsg_app_dev->parent = iucv_root;
0176 smsg_app_dev->release = (void (*)(struct device *)) kfree;
0177 smsg_app_dev->driver = smsgiucv_drv;
0178 rc = device_register(smsg_app_dev);
0179 if (rc) {
0180 put_device(smsg_app_dev);
0181 goto fail;
0182 }
0183
0184
0185 if (sender) {
0186 int len = strlen(sender);
0187 while (len--)
0188 sender[len] = toupper(sender[len]);
0189 }
0190
0191
0192 rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
0193 if (rc) {
0194 device_unregister(smsg_app_dev);
0195 goto fail;
0196 }
0197
0198 rc = 0;
0199 fail:
0200 return rc;
0201 }
0202 module_init(smsgiucv_app_init);
0203
0204 static void __exit smsgiucv_app_exit(void)
0205 {
0206
0207 smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
0208
0209
0210 cancel_work_sync(&smsg_event_work);
0211 smsg_event_work_fn(&smsg_event_work);
0212
0213 device_unregister(smsg_app_dev);
0214 }
0215 module_exit(smsgiucv_app_exit);
0216
0217 MODULE_LICENSE("GPL v2");
0218 MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
0219 MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");