Containerized syslog working properly
[safe/jmp/linux-2.6] / kernel / syslog.c
1 /*
2  *  This program is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU General Public License as
4  *  published by the Free Software Foundation, version 2 of the
5  *  License.
6  *
7  *  Jun 2010
8  *  Jean-Marc Pigeon    <jmp@safe.ca>
9  *
10  *  Purpose is to regroup all procedure involved
11  *  in system log.
12  *  System log need to be containerized to avoid
13  *  crossing over critical data between physical host layer
14  *  and container layer.
15  *
16  *  The principle is to keep a containerized ring buffer
17  *  where container kernel data are redirected, kept and
18  *  managed.
19  *
20  *  Containerized syslog is activated if CLONE_SYSLOG
21  *  condition is true.
22  *
23  */
24
25 #include <linux/module.h>
26 #include <linux/bootmem.h>
27 #include <linux/slab.h>
28 #include <linux/cred.h>
29 #include <linux/kref.h>
30 #include <linux/user_namespace.h>
31 #include <linux/syslog.h>
32
33 /*
34  * Static memory definition, used to assign a syslog
35  * to the init process itself
36  *
37  */
38 #define __LOG_BUF_LEN   (1 << CONFIG_LOG_BUF_SHIFT)
39 static char __log_buf[__LOG_BUF_LEN];
40
41 struct syslog_ns init_syslog_ns = {
42         .kref = {
43                 .refcount       = ATOMIC_INIT(2),
44         },
45         .handle = 1,    /*kernel INIT process pid       */
46         .logbuf_lock = __SPIN_LOCK_INITIALIZER(logbuf_lock),
47         .buf_len = __LOG_BUF_LEN,
48         .buf = __log_buf
49 };
50
51 /*
52  * List of all syslog ns currently allocated
53  * first member of this list (kernel syslog)
54  * can't be removed.
55  */
56 struct  log_list        {
57         spinlock_t list_lock;           /*make sure about list access   */
58         struct log_list *next;          /*next syslog_ns in the list    */
59         struct syslog_ns *syslog_ns;
60         }  log_list = {
61                 .list_lock = __SPIN_LOCK_INITIALIZER(list_lock),
62                 .next = (struct log_list *)0,
63                 .syslog_ns = &init_syslog_ns
64                 };
65 /*
66  * removing a syslog reference from the list
67  *
68  */
69 static void removing_syslog_ns(struct syslog_ns *syslog_ns)
70
71 {
72         int done;
73         struct log_list *start;
74
75         done = false;
76         start = &log_list;
77         while (start->next != (struct log_list *)0) {
78                 struct log_list *check;
79                 unsigned long flags;
80
81                 spin_lock_irqsave(&(start->next->list_lock), flags);
82                 check = start->next;
83                 if (check->syslog_ns == syslog_ns) {
84                         start->next = check->next;
85                         done = true;
86                         }
87                 spin_unlock_irqrestore(&(check->list_lock), flags);
88                 if (done == true) {
89                         kfree(check);
90                         break;
91                         }
92                 start = start->next;
93                 }
94 }
95
96 /*
97  * adding a syslog_ns to the list of known syslog
98  *
99  */
100 static void adding_syslog_ns(struct syslog_ns *syslog_ns)
101
102 {
103         int done;
104         struct log_list *start;
105
106         done = false;
107         start = &log_list;
108         while ((done == false) && (start != (struct log_list *)0)) {
109                 unsigned long flags;
110
111                 spin_lock_irqsave(&(start->list_lock), flags);
112                 if (start->next == (struct log_list *)0) {
113                         struct log_list *next;
114
115                         next = kzalloc(sizeof(struct log_list), GFP_KERNEL);
116                         BUG_ON(!next);
117                         spin_lock_init(&(next->list_lock));
118                         next->syslog_ns = syslog_ns;
119                         start->next = next;
120                         done = true;
121                         }
122                 spin_unlock_irqrestore(&(start->list_lock), flags);
123                 start = start->next;
124                 }
125 }
126 /*
127  * Procedure to free all ressources tied to syslog
128  *
129  */
130 static struct syslog_ns *free_all_syslog_ns(struct syslog_ns *syslog_ns)
131
132 {
133         if (syslog_ns != (struct syslog_ns *)0) {
134                 (void) removing_syslog_ns(syslog_ns);
135                 (void) kfree(syslog_ns->buf);
136                 (void) kfree(syslog_ns);
137                 syslog_ns = (struct syslog_ns *)0;
138                 }
139         return syslog_ns;
140 }
141
142 /*
143  * Procedure to assign memory for syslog area
144  *
145  */
146 static struct syslog_ns *malloc_syslog_ns(unsigned container_buf_len)
147 {
148         struct syslog_ns *ns;
149         char *buf;
150
151         ns = (struct syslog_ns *)0;
152         buf = (char *)0;
153
154
155         ns = kzalloc(sizeof(*ns), GFP_KERNEL);
156         buf = kzalloc(container_buf_len, GFP_KERNEL);
157         if ((!ns) || (!buf)) {
158                 kfree(buf);
159                 kfree(ns);
160                 return 0;
161                 }
162
163         kref_init(&(ns->kref));
164         spin_lock_init(&(ns->logbuf_lock));
165         ns->handle = current->pid;
166         ns->buf_len = container_buf_len;
167         ns->buf = buf;
168         (void) adding_syslog_ns(ns);
169         return ns;
170 }
171 /*
172  * Procedure to locate and return a syslog_ns with same handle as pid submitted.
173  * return a NULL pointer if not found;
174  *
175  */
176 static struct syslog_ns *find_its_syslog_ns(pid_t pid)
177
178 {
179         struct log_list *start;
180
181         start = &log_list;
182         while (start != (struct log_list *)0) {
183                 if (start->syslog_ns->handle == pid)
184                         return start->syslog_ns;
185                 start = start->next;
186                 }
187         return 0;
188 }
189 /*
190  * Procedure to ONLY increase syslog buffer size
191  * If syslog_ns is NULL, assign a brand new syslog_ns
192  *
193  */
194 struct syslog_ns *resize_syslog_ns(struct syslog_ns *syslog_ns,
195                         unsigned container_buf_len)
196
197 {
198         if ((syslog_ns == &init_syslog_ns) &&
199                 (container_buf_len > syslog_ns->buf_len)) {
200                 int old_buf_len;
201                 char *old_buf;
202                 char *new_buf;
203                 unsigned long flags;
204
205                 old_buf_len = syslog_ns->buf_len;
206                 old_buf = syslog_ns->buf;
207                 new_buf = alloc_bootmem(container_buf_len);
208                 if (!new_buf) {
209                         (void) printk(KERN_WARNING
210                                 "log_buf_len: allocation failed\n");
211                         return ERR_PTR(-ENOMEM);
212                         }
213                 spin_lock_irqsave(&(syslog_ns->logbuf_lock), flags);
214                 (void) memmove(new_buf, old_buf, old_buf_len);
215                 syslog_ns->buf = new_buf;
216                 syslog_ns->buf_len = container_buf_len;
217                 spin_unlock_irqrestore(&(syslog_ns->logbuf_lock), flags);
218                 if (old_buf != __log_buf)
219                         (void) free_bootmem((unsigned long)old_buf,
220                                             old_buf_len);
221                 }
222         if (!syslog_ns)
223                 return malloc_syslog_ns(container_buf_len);
224         if (syslog_ns->buf_len > container_buf_len) {
225                 (void) printk(KERN_WARNING "log_buf_len: Not allowed "
226                                         "to decrease syslog buffer\n");
227                 return ERR_PTR(-EINVAL);
228                 }
229         if (syslog_ns->buf_len < container_buf_len) {
230                 char *old_buf;
231                 char *new_buf;
232                 unsigned long flags;
233
234                 old_buf = syslog_ns->buf;
235                 new_buf = kzalloc(container_buf_len, GFP_KERNEL);
236                 if (!new_buf)
237                         return ERR_PTR(-ENOMEM);
238                 spin_lock_irqsave(&(syslog_ns->logbuf_lock), flags);
239                 (void) memmove(new_buf, old_buf, syslog_ns->buf_len);
240                 syslog_ns->buf = new_buf;
241                 syslog_ns->buf_len = container_buf_len;
242                 spin_unlock_irqrestore(&(syslog_ns->logbuf_lock), flags);
243                 (void) kfree(old_buf);
244                 }
245         (void) printk(KERN_NOTICE "log_buf_len: %u\n", syslog_ns->buf_len);
246         return syslog_ns;
247 }
248
249 /*
250  * Procedure to use current syslog unless a CLONE_SYSLOG is set
251  * such a new syslog area is defined and used
252  *
253  */
254 struct syslog_ns *copy_syslog_ns(unsigned long flags,
255                                 struct syslog_ns *current_syslog_ns)
256
257 {
258 /*4096 should be enough for container syslog    */
259 #define CONTAINER_BUF_LEN       4096
260
261         BUG_ON(!current_syslog_ns);
262         if ((flags & CLONE_SYSLOG) == CLONE_SYSLOG)
263                 current_syslog_ns = malloc_syslog_ns(CONTAINER_BUF_LEN);
264         else
265                 /*incrementing usage ref count  */
266                 (void) kref_get(&(current_syslog_ns->kref));
267         return current_syslog_ns;
268 }
269
270 /*
271  * Procedure to decrement syslog usage count and free memory
272  * if syslog usage count reach zero.
273  *
274  */
275 void free_syslog_ns(struct kref *kref)
276
277 {
278         struct syslog_ns *sl;
279
280         sl = container_of(kref, struct syslog_ns, kref);
281         sl = free_all_syslog_ns(sl);
282 }
283
284 /*
285  * Procedure to get the current syslog area linked to a
286  * container (by CLONE_SYSLOG).
287  * if trouble, report host kernel own syslog_ns.
288  *
289  */
290 struct syslog_ns *current_syslog_ns(void)
291
292 {
293
294         struct syslog_ns *ns;
295
296         ns = (struct syslog_ns *)0;
297         if (current->nsproxy)
298                 ns = current->nsproxy->syslog_ns;
299         if (!ns)        /*lets try to cover log anyway! */
300                 ns = &init_syslog_ns;
301         return ns;
302 }
303 /*
304  * Procedure to replace current syslog namespace with another.
305  * Return the original current syslog_ns.
306  *
307  */
308 struct syslog_ns *switch_syslog_ns(struct syslog_ns *syslog_ns)
309
310 {
311         unsigned long flags;
312         struct syslog_ns *old;
313
314         spin_lock_irqsave(&(current_syslog_ns()->logbuf_lock), flags);
315         old = current_syslog_ns();
316         if (syslog_ns)
317                 current->nsproxy->syslog_ns = syslog_ns;
318         spin_unlock_irqrestore(&(old->logbuf_lock), flags);
319         return old;
320 }
321
322 /*
323  * Procedure to locate the syslog handle own by a given
324  * pid or one of its parents lineage.
325  *
326  */
327 struct syslog_ns *find_syslog_ns_bypid(pid_t pid)
328
329 {
330         while (pid > 1) {
331                 struct syslog_ns *its_ns;
332                 struct task_struct *ns_task;
333
334                 its_ns = find_its_syslog_ns(pid);
335                 if (its_ns)
336                         return its_ns;
337                 ns_task = find_task_by_vpid(pid);
338                 if ((ns_task) && (ns_task->real_parent))
339                         pid = ns_task->real_parent->pid;
340                 else
341                         break;
342                 }
343         return log_list.syslog_ns;
344 }
345
346 /*
347  * Procedure to dereference syslog_ns usage, if no reference
348  * anymore, memory is freed.
349  *
350  */
351 void put_syslog_ns(struct syslog_ns *ns)
352
353 {
354         kref_put(&ns->kref, free_syslog_ns);
355 }