diff options
| -rw-r--r-- | fs/proc/proc_sysctl.c | 45 | ||||
| -rw-r--r-- | include/linux/sysctl.h | 22 | ||||
| -rw-r--r-- | include/linux/utsname.h | 16 | ||||
| -rw-r--r-- | kernel/sys.c | 2 | ||||
| -rw-r--r-- | kernel/utsname_sysctl.c | 23 |
5 files changed, 108 insertions, 0 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index dacd840a675a..df594803f45a 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | */ | 3 | */ |
| 4 | #include <linux/init.h> | 4 | #include <linux/init.h> |
| 5 | #include <linux/sysctl.h> | 5 | #include <linux/sysctl.h> |
| 6 | #include <linux/poll.h> | ||
| 6 | #include <linux/proc_fs.h> | 7 | #include <linux/proc_fs.h> |
| 7 | #include <linux/security.h> | 8 | #include <linux/security.h> |
| 8 | #include <linux/namei.h> | 9 | #include <linux/namei.h> |
| @@ -14,6 +15,15 @@ static const struct inode_operations proc_sys_inode_operations; | |||
| 14 | static const struct file_operations proc_sys_dir_file_operations; | 15 | static const struct file_operations proc_sys_dir_file_operations; |
| 15 | static const struct inode_operations proc_sys_dir_operations; | 16 | static const struct inode_operations proc_sys_dir_operations; |
| 16 | 17 | ||
| 18 | void proc_sys_poll_notify(struct ctl_table_poll *poll) | ||
| 19 | { | ||
| 20 | if (!poll) | ||
| 21 | return; | ||
| 22 | |||
| 23 | atomic_inc(&poll->event); | ||
| 24 | wake_up_interruptible(&poll->wait); | ||
| 25 | } | ||
| 26 | |||
| 17 | static struct inode *proc_sys_make_inode(struct super_block *sb, | 27 | static struct inode *proc_sys_make_inode(struct super_block *sb, |
| 18 | struct ctl_table_header *head, struct ctl_table *table) | 28 | struct ctl_table_header *head, struct ctl_table *table) |
| 19 | { | 29 | { |
| @@ -176,6 +186,39 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf, | |||
| 176 | return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 1); | 186 | return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 1); |
| 177 | } | 187 | } |
| 178 | 188 | ||
| 189 | static int proc_sys_open(struct inode *inode, struct file *filp) | ||
| 190 | { | ||
| 191 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; | ||
| 192 | |||
| 193 | if (table->poll) | ||
| 194 | filp->private_data = proc_sys_poll_event(table->poll); | ||
| 195 | |||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) | ||
| 200 | { | ||
| 201 | struct inode *inode = filp->f_path.dentry->d_inode; | ||
| 202 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; | ||
| 203 | unsigned long event = (unsigned long)filp->private_data; | ||
| 204 | unsigned int ret = DEFAULT_POLLMASK; | ||
| 205 | |||
| 206 | if (!table->proc_handler) | ||
| 207 | goto out; | ||
| 208 | |||
| 209 | if (!table->poll) | ||
| 210 | goto out; | ||
| 211 | |||
| 212 | poll_wait(filp, &table->poll->wait, wait); | ||
| 213 | |||
| 214 | if (event != atomic_read(&table->poll->event)) { | ||
| 215 | filp->private_data = proc_sys_poll_event(table->poll); | ||
| 216 | ret = POLLIN | POLLRDNORM | POLLERR | POLLPRI; | ||
| 217 | } | ||
| 218 | |||
| 219 | out: | ||
| 220 | return ret; | ||
| 221 | } | ||
| 179 | 222 | ||
| 180 | static int proc_sys_fill_cache(struct file *filp, void *dirent, | 223 | static int proc_sys_fill_cache(struct file *filp, void *dirent, |
| 181 | filldir_t filldir, | 224 | filldir_t filldir, |
| @@ -364,6 +407,8 @@ static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct | |||
| 364 | } | 407 | } |
| 365 | 408 | ||
| 366 | static const struct file_operations proc_sys_file_operations = { | 409 | static const struct file_operations proc_sys_file_operations = { |
| 410 | .open = proc_sys_open, | ||
| 411 | .poll = proc_sys_poll, | ||
| 367 | .read = proc_sys_read, | 412 | .read = proc_sys_read, |
| 368 | .write = proc_sys_write, | 413 | .write = proc_sys_write, |
| 369 | .llseek = default_llseek, | 414 | .llseek = default_llseek, |
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 9a1ec10fd504..703cfa33a3ca 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
| @@ -931,6 +931,7 @@ enum | |||
| 931 | #ifdef __KERNEL__ | 931 | #ifdef __KERNEL__ |
| 932 | #include <linux/list.h> | 932 | #include <linux/list.h> |
| 933 | #include <linux/rcupdate.h> | 933 | #include <linux/rcupdate.h> |
| 934 | #include <linux/wait.h> | ||
| 934 | 935 | ||
| 935 | /* For the /proc/sys support */ | 936 | /* For the /proc/sys support */ |
| 936 | struct ctl_table; | 937 | struct ctl_table; |
| @@ -1011,6 +1012,26 @@ extern int proc_do_large_bitmap(struct ctl_table *, int, | |||
| 1011 | * cover common cases. | 1012 | * cover common cases. |
| 1012 | */ | 1013 | */ |
| 1013 | 1014 | ||
| 1015 | /* Support for userspace poll() to watch for changes */ | ||
| 1016 | struct ctl_table_poll { | ||
| 1017 | atomic_t event; | ||
| 1018 | wait_queue_head_t wait; | ||
| 1019 | }; | ||
| 1020 | |||
| 1021 | static inline void *proc_sys_poll_event(struct ctl_table_poll *poll) | ||
| 1022 | { | ||
| 1023 | return (void *)(unsigned long)atomic_read(&poll->event); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | void proc_sys_poll_notify(struct ctl_table_poll *poll); | ||
| 1027 | |||
| 1028 | #define __CTL_TABLE_POLL_INITIALIZER(name) { \ | ||
| 1029 | .event = ATOMIC_INIT(0), \ | ||
| 1030 | .wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.wait) } | ||
| 1031 | |||
| 1032 | #define DEFINE_CTL_TABLE_POLL(name) \ | ||
| 1033 | struct ctl_table_poll name = __CTL_TABLE_POLL_INITIALIZER(name) | ||
| 1034 | |||
| 1014 | /* A sysctl table is an array of struct ctl_table: */ | 1035 | /* A sysctl table is an array of struct ctl_table: */ |
| 1015 | struct ctl_table | 1036 | struct ctl_table |
| 1016 | { | 1037 | { |
| @@ -1021,6 +1042,7 @@ struct ctl_table | |||
| 1021 | struct ctl_table *child; | 1042 | struct ctl_table *child; |
| 1022 | struct ctl_table *parent; /* Automatically set */ | 1043 | struct ctl_table *parent; /* Automatically set */ |
| 1023 | proc_handler *proc_handler; /* Callback for text formatting */ | 1044 | proc_handler *proc_handler; /* Callback for text formatting */ |
| 1045 | struct ctl_table_poll *poll; | ||
| 1024 | void *extra1; | 1046 | void *extra1; |
| 1025 | void *extra2; | 1047 | void *extra2; |
| 1026 | }; | 1048 | }; |
diff --git a/include/linux/utsname.h b/include/linux/utsname.h index 4e5b0213fdc1..c714ed75eae2 100644 --- a/include/linux/utsname.h +++ b/include/linux/utsname.h | |||
| @@ -37,6 +37,14 @@ struct new_utsname { | |||
| 37 | #include <linux/nsproxy.h> | 37 | #include <linux/nsproxy.h> |
| 38 | #include <linux/err.h> | 38 | #include <linux/err.h> |
| 39 | 39 | ||
| 40 | enum uts_proc { | ||
| 41 | UTS_PROC_OSTYPE, | ||
| 42 | UTS_PROC_OSRELEASE, | ||
| 43 | UTS_PROC_VERSION, | ||
| 44 | UTS_PROC_HOSTNAME, | ||
| 45 | UTS_PROC_DOMAINNAME, | ||
| 46 | }; | ||
| 47 | |||
| 40 | struct user_namespace; | 48 | struct user_namespace; |
| 41 | extern struct user_namespace init_user_ns; | 49 | extern struct user_namespace init_user_ns; |
| 42 | 50 | ||
| @@ -80,6 +88,14 @@ static inline struct uts_namespace *copy_utsname(unsigned long flags, | |||
| 80 | } | 88 | } |
| 81 | #endif | 89 | #endif |
| 82 | 90 | ||
| 91 | #ifdef CONFIG_PROC_SYSCTL | ||
| 92 | extern void uts_proc_notify(enum uts_proc proc); | ||
| 93 | #else | ||
| 94 | static inline void uts_proc_notify(enum uts_proc proc) | ||
| 95 | { | ||
| 96 | } | ||
| 97 | #endif | ||
| 98 | |||
| 83 | static inline struct new_utsname *utsname(void) | 99 | static inline struct new_utsname *utsname(void) |
| 84 | { | 100 | { |
| 85 | return ¤t->nsproxy->uts_ns->name; | 101 | return ¤t->nsproxy->uts_ns->name; |
diff --git a/kernel/sys.c b/kernel/sys.c index 58459509b14c..d06c091e0345 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
| @@ -1286,6 +1286,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) | |||
| 1286 | memset(u->nodename + len, 0, sizeof(u->nodename) - len); | 1286 | memset(u->nodename + len, 0, sizeof(u->nodename) - len); |
| 1287 | errno = 0; | 1287 | errno = 0; |
| 1288 | } | 1288 | } |
| 1289 | uts_proc_notify(UTS_PROC_HOSTNAME); | ||
| 1289 | up_write(&uts_sem); | 1290 | up_write(&uts_sem); |
| 1290 | return errno; | 1291 | return errno; |
| 1291 | } | 1292 | } |
| @@ -1336,6 +1337,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len) | |||
| 1336 | memset(u->domainname + len, 0, sizeof(u->domainname) - len); | 1337 | memset(u->domainname + len, 0, sizeof(u->domainname) - len); |
| 1337 | errno = 0; | 1338 | errno = 0; |
| 1338 | } | 1339 | } |
| 1340 | uts_proc_notify(UTS_PROC_DOMAINNAME); | ||
| 1339 | up_write(&uts_sem); | 1341 | up_write(&uts_sem); |
| 1340 | return errno; | 1342 | return errno; |
| 1341 | } | 1343 | } |
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c index a2cd77e70d4d..3b0d48ebf81d 100644 --- a/kernel/utsname_sysctl.c +++ b/kernel/utsname_sysctl.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <linux/uts.h> | 13 | #include <linux/uts.h> |
| 14 | #include <linux/utsname.h> | 14 | #include <linux/utsname.h> |
| 15 | #include <linux/sysctl.h> | 15 | #include <linux/sysctl.h> |
| 16 | #include <linux/wait.h> | ||
| 16 | 17 | ||
| 17 | static void *get_uts(ctl_table *table, int write) | 18 | static void *get_uts(ctl_table *table, int write) |
| 18 | { | 19 | { |
| @@ -51,12 +52,19 @@ static int proc_do_uts_string(ctl_table *table, int write, | |||
| 51 | uts_table.data = get_uts(table, write); | 52 | uts_table.data = get_uts(table, write); |
| 52 | r = proc_dostring(&uts_table,write,buffer,lenp, ppos); | 53 | r = proc_dostring(&uts_table,write,buffer,lenp, ppos); |
| 53 | put_uts(table, write, uts_table.data); | 54 | put_uts(table, write, uts_table.data); |
| 55 | |||
| 56 | if (write) | ||
| 57 | proc_sys_poll_notify(table->poll); | ||
| 58 | |||
| 54 | return r; | 59 | return r; |
| 55 | } | 60 | } |
| 56 | #else | 61 | #else |
| 57 | #define proc_do_uts_string NULL | 62 | #define proc_do_uts_string NULL |
| 58 | #endif | 63 | #endif |
| 59 | 64 | ||
| 65 | static DEFINE_CTL_TABLE_POLL(hostname_poll); | ||
| 66 | static DEFINE_CTL_TABLE_POLL(domainname_poll); | ||
| 67 | |||
| 60 | static struct ctl_table uts_kern_table[] = { | 68 | static struct ctl_table uts_kern_table[] = { |
| 61 | { | 69 | { |
| 62 | .procname = "ostype", | 70 | .procname = "ostype", |
| @@ -85,6 +93,7 @@ static struct ctl_table uts_kern_table[] = { | |||
| 85 | .maxlen = sizeof(init_uts_ns.name.nodename), | 93 | .maxlen = sizeof(init_uts_ns.name.nodename), |
| 86 | .mode = 0644, | 94 | .mode = 0644, |
| 87 | .proc_handler = proc_do_uts_string, | 95 | .proc_handler = proc_do_uts_string, |
| 96 | .poll = &hostname_poll, | ||
| 88 | }, | 97 | }, |
| 89 | { | 98 | { |
| 90 | .procname = "domainname", | 99 | .procname = "domainname", |
| @@ -92,6 +101,7 @@ static struct ctl_table uts_kern_table[] = { | |||
| 92 | .maxlen = sizeof(init_uts_ns.name.domainname), | 101 | .maxlen = sizeof(init_uts_ns.name.domainname), |
| 93 | .mode = 0644, | 102 | .mode = 0644, |
| 94 | .proc_handler = proc_do_uts_string, | 103 | .proc_handler = proc_do_uts_string, |
| 104 | .poll = &domainname_poll, | ||
| 95 | }, | 105 | }, |
| 96 | {} | 106 | {} |
| 97 | }; | 107 | }; |
| @@ -105,6 +115,19 @@ static struct ctl_table uts_root_table[] = { | |||
| 105 | {} | 115 | {} |
| 106 | }; | 116 | }; |
| 107 | 117 | ||
| 118 | #ifdef CONFIG_PROC_SYSCTL | ||
| 119 | /* | ||
| 120 | * Notify userspace about a change in a certain entry of uts_kern_table, | ||
| 121 | * identified by the parameter proc. | ||
| 122 | */ | ||
| 123 | void uts_proc_notify(enum uts_proc proc) | ||
| 124 | { | ||
| 125 | struct ctl_table *table = &uts_kern_table[proc]; | ||
| 126 | |||
| 127 | proc_sys_poll_notify(table->poll); | ||
| 128 | } | ||
| 129 | #endif | ||
| 130 | |||
| 108 | static int __init utsname_sysctl_init(void) | 131 | static int __init utsname_sysctl_init(void) |
| 109 | { | 132 | { |
| 110 | register_sysctl_table(uts_root_table); | 133 | register_sysctl_table(uts_root_table); |
