532377cf49d102ef355c1cee5fde6412b30f4e71
[linux-flexiantxendom0-3.2.10.git] / kernel / evlposix.c
1 /*
2  * Linux Event Logging
3  * Copyright (C) International Business Machines Corp., 2001
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  *  Please send e-mail to kenistoj@users.sourceforge.net if you have
20  *  questions or comments.
21  *
22  *  Project Website:  http://evlog.sourceforge.net/
23  */
24
25 #include <linux/config.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/spinlock.h>
29 #include <linux/string.h>
30 #include <linux/ctype.h>
31 #include <linux/slab.h>
32 #include <linux/errno.h>
33 #include <linux/evl_log.h>
34
35 /*
36  * This file implements the "legacy" functions defined in early releases of
37  * Event Logging.  Consider using the newer functions declared in evlog.h --
38  * e.g., evl_write(), evl_printk(), evl_vprintk().
39  */
40
41 extern spinlock_t evl_msgbuf_lock;
42 extern char evl_msgbuf[];
43
44 enum base_type {
45         TY_NONE,
46         TY_CHAR,
47         TY_UCHAR,
48         TY_SHORT,
49         TY_USHORT,
50         TY_INT,
51         TY_UINT,
52         TY_LONG,
53         TY_ULONG,
54         TY_LONGLONG,
55         TY_ULONGLONG,
56         TY_STRING,
57         TY_ADDRESS
58 };
59
60 static struct type_info {
61         size_t  ti_size;
62         char    *ti_name;
63 } type_info[] = {
64         {0,                     "none"},
65         {sizeof(char),          "char"},
66         {sizeof(unsigned char), "uchar"},
67         {sizeof(short),         "short"},
68         {sizeof(unsigned short),"ushort"},
69         {sizeof(int),           "int"},
70         {sizeof(unsigned int),  "uint"},
71         {sizeof(long),          "long"},
72         {sizeof(unsigned long), "ulong"},
73         {sizeof(long long),     "longlong"},
74         {sizeof(unsigned long long),    "ulonglong"},
75         {0,                     "string"},
76         {sizeof(void*),         "address"},
77         {0,                     NULL}
78 };
79
80 struct att_type_info {
81         enum base_type  at_type;    /* TY_INT for "int", "int[]", or "2*int" */
82         int             at_nelements;   /* 5 for "5*int */
83         int             at_array;       /* 1 (true) for "int[]" */
84 };
85
86 static enum base_type
87 get_type_by_name(const char *name)
88 {
89         enum base_type i;
90         for (i = TY_NONE+1; type_info[i].ti_name; i++) {
91                 if (!strcmp(name, type_info[i].ti_name)) {
92                         return i;
93                 }
94         }
95         return TY_NONE;
96 }
97
98 /*
99  * att_type should be a type spec such as "int", "int[]", or "5*int".  Parse it
100  * and fill in *ti accordingly.  Returns 0 on success, -1 on failure.
101  */
102 static int
103 parse_att_type(const char *att_type, struct att_type_info *ti)
104 {
105         const char *s, *left_bracket;
106         const char *type_name;
107 #define MAX_TYPE_NAME_LEN 20
108         char name_buf[MAX_TYPE_NAME_LEN+1];
109
110         if (isdigit(att_type[0])) {
111                 /* "5*int" */
112                 ti->at_nelements =
113                         (int) simple_strtoul(att_type, (char**) &s, 10);
114                 if (*s != '*') {
115                         return -1;
116                 }
117                 type_name = s+1;
118                 ti->at_array = 0;
119         } else if ((left_bracket = strchr(att_type, '[')) != NULL) {
120                 /* int[] */
121                 size_t name_len;
122                 ti->at_array = 1;
123                 ti->at_nelements = 0;
124                 if (0 != strcmp(left_bracket, "[]")) {
125                         return -1;
126                 }
127                 /* Copy the name to name_buf and point type_name at it. */
128                 type_name = name_buf;
129                 name_len = left_bracket - att_type;
130                 if (name_len == 0 || name_len > MAX_TYPE_NAME_LEN) {
131                         return -1;
132                 }
133                 (void) memcpy(name_buf, att_type, name_len);
134                 name_buf[name_len] = '\0';
135         } else {
136                 /* "int" */
137                 type_name = att_type;
138                 ti->at_array = 0;
139                 ti->at_nelements = 1;
140         }
141         ti->at_type = get_type_by_name(type_name);
142         return (ti->at_type == TY_NONE ? -1 : 0);
143 }
144
145 /*
146  * COPYARGS copies n args of type lt (little type) from the stack into
147  * buffer b.  bt (big type) is the type of the arg as it appears on the stack.
148  */
149 #define COPYARGS(lt,bt) \
150 { \
151         while(n-- > 0) { \
152                 lt v=(lt)va_arg(args,bt); \
153                 evl_put(b, &v, sizeof(lt)); \
154         } \
155 }
156
157 #ifdef CONFIG_ARCH_S390X
158 #define INTARG long
159 #else
160 #define INTARG int
161 #endif
162
163 /**
164  * pack_typed_args() - Does most of the work of evl_writek.
165  */
166 static int
167 pack_typed_args(struct evl_recbuf *b, va_list args)
168 {
169         char *att_type;
170
171         while ((att_type = va_arg(args, char*)) &&
172             (0 != strcmp(att_type, "endofdata"))) {
173                 struct att_type_info ti;
174                 if (parse_att_type(att_type, &ti) == -1) {
175                         return -EINVAL;
176                 }
177                 if (ti.at_array) {
178                         char *array;
179                         size_t size = type_info[ti.at_type].ti_size;
180                         int n;
181
182                         /* Next arg is the array size. */
183                         n = va_arg(args, INTARG);
184                         /* Next arg is the array address. */
185                         array = (char*) va_arg(args, void*);
186
187                         switch (ti.at_type) {
188                         case TY_STRING:
189                             {
190                                 /* array points to an array of char* */
191                                 char **sarray = (char**)array;
192                                 int i;
193                                 for (i = 0; i < n; i++) {
194                                         evl_puts(b, sarray[i], 1);
195                                 }
196                                 break;
197                             }
198                         default:
199                                 evl_put(b, array, n*size);
200                                 break;
201                         }
202                 } else {
203                         /*
204                          * One or more args of the same type.
205                          */
206                         int n = ti.at_nelements;
207                         switch (ti.at_type) {
208                         case TY_CHAR:
209                         case TY_UCHAR:
210                                 COPYARGS(char, INTARG)
211                                 break;
212                         case TY_SHORT:
213                         case TY_USHORT:
214                                 COPYARGS(short, INTARG)
215                                 break;
216                         case TY_INT:
217                         case TY_UINT:
218                                 COPYARGS(int, INTARG)
219                                 break;
220                         case TY_LONG:
221                         case TY_ULONG:
222                                 COPYARGS(long, long)
223                                 break;
224                         case TY_LONGLONG:
225                         case TY_ULONGLONG:
226                                 COPYARGS(long long, long long)
227                                 break;
228                         case TY_ADDRESS:
229                                 COPYARGS(void*, void*)
230                                 break;
231                         case TY_STRING:
232                             {
233                                 char *s;
234                                 while (n-- > 0) {
235                                         s = (char *) va_arg(args, char*);
236                                         evl_puts(b, s, 1);
237                                 }
238                                 break;
239                             }
240                         default:
241                                 break;
242                         } /* end of switch */
243                 } /* not array */
244         } /* next att_type */
245
246         return 0;
247 }
248
249 /*
250  * These functions are used for logging events with log_format of
251  * EVL_BINARY.  See event logging specification at
252  * http://evlog.sourceforge.net/linuxEvlog.html
253  * for details.
254  */
255 int evl_writek(posix_log_facility_t facility, int event_type,
256         posix_log_severity_t severity, unsigned int flags, ...)
257 {
258         va_list args;
259         int ret = 0;
260
261         va_start(args, flags);
262         ret = evl_vwritek(facility, event_type, severity, flags, args);
263         va_end(args);
264
265         return ret;
266 }
267
268 int evl_vwritek(posix_log_facility_t facility, int event_type,
269         posix_log_severity_t severity, unsigned int flags, va_list args)
270 {
271         unsigned long iflags;
272         struct evl_recbuf b;
273         int ret;
274         size_t reclen;
275
276         spin_lock_irqsave(&evl_msgbuf_lock, iflags);
277         evl_init_recbuf(&b, evl_msgbuf, EVL_ENTRY_MAXLEN);
278         ret = pack_typed_args(&b, args);
279         if (ret == 0) {
280                 reclen = evl_datasz(&b, &flags);
281                 ret = evl_write(facility, event_type, severity,
282                         b.b_buf, reclen, flags, EVL_BINARY);
283         }
284         spin_unlock_irqrestore(&evl_msgbuf_lock, iflags);
285         return ret;
286 }
287
288 /*
289  * These functions are used for logging events with log_format of
290  * EVL_STRING.  See event logging specification at
291  * http://evlog.sourceforge.net/linuxEvlog.html
292  * for details.
293  */
294 int posix_log_printf(posix_log_facility_t facility, int event_type,
295         posix_log_severity_t severity, unsigned int flags, const char *fmt, ...)
296 {
297         int ret = 0;
298         va_list args;
299
300         if (!fmt) {
301                 return evl_write(facility, event_type, severity, NULL, 0,
302                         flags, EVL_NODATA);
303         }
304
305         va_start(args, fmt);
306         ret = posix_log_vprintf(facility, event_type, severity, flags, fmt,
307                 args);
308         va_end(args);
309         return ret;
310 }
311
312 int posix_log_vprintf(posix_log_facility_t facility, int event_type,
313         posix_log_severity_t severity, unsigned int flags, const char *fmt,
314         va_list args)
315 {
316         size_t recsize;
317         int ret;
318         unsigned long iflags;
319
320         if (!fmt) {
321                 return evl_write(facility, event_type, severity, NULL, 0,
322                         flags, EVL_NODATA);
323         }
324
325         spin_lock_irqsave(&evl_msgbuf_lock, iflags);
326         recsize = 1 + vsnprintf(evl_msgbuf, EVL_ENTRY_MAXLEN, fmt, args);
327         if (recsize > EVL_ENTRY_MAXLEN) {
328                 recsize = EVL_ENTRY_MAXLEN;
329                 flags |= EVL_TRUNCATE;
330         }
331         ret = evl_write(facility, event_type, severity, evl_msgbuf, recsize,
332                 flags, EVL_STRING);
333         spin_unlock_irqrestore(&evl_msgbuf_lock, iflags);
334         return ret;
335 }
336
337 /*
338  * This is the standard POSIX function for writing events to the event log.
339  * See event logging specification at:
340  * http://evlog.sourceforge.net/linuxEvlog.html
341  */
342 int posix_log_write(posix_log_facility_t facility, int event_type,
343         posix_log_severity_t severity, const void *buf,
344         size_t recsize, int format, unsigned int flags)
345 {
346         if (!buf || recsize == 0 || format == EVL_NODATA) {
347                 buf = NULL;
348                 recsize = 0;
349                 format = EVL_NODATA;
350         }
351         if (format == EVL_STRING && strlen((const char*)buf) != recsize-1) {
352                 return -EBADMSG;
353         }
354
355         return evl_write(facility, event_type, severity, buf, recsize, flags,
356                 format);
357 }
358
359 /**
360  * evl_gen_facility_code() - Generate facility "code" from facility name
361  * The code is just a strdup of the name.
362  */
363 int evl_gen_facility_code(const char *fname, posix_log_facility_t *fcode)
364 {
365         size_t name_len;
366         char *s;
367
368         if (!fname || !fcode) {
369                 return -EINVAL;
370         }
371
372         name_len = strlen(fname);
373         if (name_len == 0 || name_len >= POSIX_LOG_MEMSTR_MAXLEN) {
374                 return -EINVAL;
375         }
376
377         s = kmalloc(name_len+1, GFP_KERNEL);
378         if (!s) {
379                 return -ENOMEM;
380         }
381         (void) strcpy(s, fname);
382         *fcode = s;
383         return 0;
384 }
385
386 /**
387  * evl_register_facility() - Generate "code" from name; pretend to register
388  * We no longer register the facility from the kernel.
389  */
390 int evl_register_facility(const char *fname, posix_log_facility_t *fcode)
391 {
392         return evl_gen_facility_code(fname, fcode);
393 }
394
395 EXPORT_SYMBOL(evl_writek);
396 EXPORT_SYMBOL(evl_vwritek);
397 EXPORT_SYMBOL(posix_log_write);
398 EXPORT_SYMBOL(posix_log_printf);
399 EXPORT_SYMBOL(posix_log_vprintf);
400 EXPORT_SYMBOL(evl_gen_facility_code);
401 EXPORT_SYMBOL(evl_register_facility);