1/*
2 * UNIX Syslog extension for Ruby
3 * Amos Gouaux, University of Texas at Dallas
4 * <amos+ruby@utdallas.edu>
5 * Documented by mathew <meta@pobox.com>
6 *
7 * $RoughId: syslog.c,v 1.21 2002/02/25 12:21:17 knu Exp $
8 * $Id: syslog.c 44659 2014-01-19 16:28:53Z nagachika $
9 */
10
11#include "ruby/ruby.h"
12#include "ruby/util.h"
13#include <syslog.h>
14
15/* Syslog class */
16static VALUE mSyslog;
17/*
18 * Module holding all Syslog constants.  See Syslog::log and
19 * Syslog::open for constant descriptions.
20 */
21static VALUE mSyslogConstants;
22/* Module holding Syslog option constants */
23static VALUE mSyslogOption;
24/* Module holding Syslog facility constants */
25static VALUE mSyslogFacility;
26/* Module holding Syslog level constants */
27static VALUE mSyslogLevel;
28/* Module holding Syslog utility macros */
29static VALUE mSyslogMacros;
30
31static const char *syslog_ident = NULL;
32static int syslog_options = -1, syslog_facility = -1, syslog_mask = -1;
33static int syslog_opened = 0;
34
35/* Package helper routines */
36static void syslog_write(int pri, int argc, VALUE *argv)
37{
38    VALUE str;
39
40    rb_secure(4);
41    if (argc < 1) {
42        rb_raise(rb_eArgError, "no log message supplied");
43    }
44
45    if (!syslog_opened) {
46        rb_raise(rb_eRuntimeError, "must open syslog before write");
47    }
48
49    str = rb_f_sprintf(argc, argv);
50
51    syslog(pri, "%s", RSTRING_PTR(str));
52}
53
54/* Closes the syslog facility.
55 * Raises a runtime exception if it is not open.
56 */
57static VALUE mSyslog_close(VALUE self)
58{
59    rb_secure(4);
60    if (!syslog_opened) {
61        rb_raise(rb_eRuntimeError, "syslog not opened");
62    }
63
64    closelog();
65
66    xfree((void *)syslog_ident);
67    syslog_ident = NULL;
68    syslog_options = syslog_facility = syslog_mask = -1;
69    syslog_opened = 0;
70
71    return Qnil;
72}
73
74/* call-seq:
75 *   open(ident, options, facility) => syslog
76 *
77 * :yields: syslog
78 *
79 * Open the syslog facility.
80 * Raises a runtime exception if it is already open.
81 *
82 * Can be called with or without a code block. If called with a block, the
83 * Syslog object created is passed to the block.
84 *
85 * If the syslog is already open, raises a RuntimeError.
86 *
87 * +ident+ is a String which identifies the calling program.
88 *
89 * +options+ is the logical OR of any of the following:
90 *
91 * LOG_CONS:: If there is an error while sending to the system logger,
92 *            write directly to the console instead.
93 *
94 * LOG_NDELAY:: Open the connection now, rather than waiting for the first
95 *              message to be written.
96 *
97 * LOG_NOWAIT:: Don't wait for any child processes created while logging
98 *              messages. (Has no effect on Linux.)
99 *
100 * LOG_ODELAY:: Opposite of LOG_NDELAY; wait until a message is sent before
101 *              opening the connection. (This is the default.)
102 *
103 * LOG_PERROR:: Print the message to stderr as well as sending it to syslog.
104 *              (Not in POSIX.1-2001.)
105 *
106 * LOG_PID:: Include the current process ID with each message.
107 *
108 * +facility+ describes the type of program opening the syslog, and is
109 * the logical OR of any of the following which are defined for the host OS:
110 *
111 * LOG_AUTH:: Security or authorization. Deprecated, use LOG_AUTHPRIV
112 *            instead.
113 *
114 * LOG_AUTHPRIV:: Security or authorization messages which should be kept
115 *                private.
116 *
117 * LOG_CONSOLE:: System console message.
118 *
119 * LOG_CRON:: System task scheduler (cron or at).
120 *
121 * LOG_DAEMON:: A system daemon which has no facility value of its own.
122 *
123 * LOG_FTP:: An FTP server.
124 *
125 * LOG_KERN:: A kernel message (not sendable by user processes, so not of
126 *            much use to Ruby, but listed here for completeness).
127 *
128 * LOG_LPR:: Line printer subsystem.
129 *
130 * LOG_MAIL:: Mail delivery or transport subsystem.
131 *
132 * LOG_NEWS:: Usenet news system.
133 *
134 * LOG_NTP:: Network Time Protocol server.
135 *
136 * LOG_SECURITY:: General security message.
137 *
138 * LOG_SYSLOG:: Messages generated internally by syslog.
139 *
140 * LOG_USER:: Generic user-level message.
141 *
142 * LOG_UUCP:: UUCP subsystem.
143 *
144 * LOG_LOCAL0 to LOG_LOCAL7:: Locally-defined facilities.
145 *
146 * Example:
147 *
148 *  Syslog.open("webrick", Syslog::LOG_PID,
149 *              Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3)
150 *
151 */
152static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
153{
154    VALUE ident, opt, fac;
155
156    if (syslog_opened) {
157        rb_raise(rb_eRuntimeError, "syslog already open");
158    }
159
160    rb_scan_args(argc, argv, "03", &ident, &opt, &fac);
161
162    if (NIL_P(ident)) {
163        ident = rb_gv_get("$0");
164    }
165    SafeStringValue(ident);
166    syslog_ident = strdup(RSTRING_PTR(ident));
167
168    if (NIL_P(opt)) {
169	syslog_options = LOG_PID | LOG_CONS;
170    } else {
171	syslog_options = NUM2INT(opt);
172    }
173
174    if (NIL_P(fac)) {
175	syslog_facility = LOG_USER;
176    } else {
177	syslog_facility = NUM2INT(fac);
178    }
179
180    openlog(syslog_ident, syslog_options, syslog_facility);
181
182    syslog_opened = 1;
183
184    setlogmask(syslog_mask = setlogmask(0));
185
186    /* be like File.new.open {...} */
187    if (rb_block_given_p()) {
188        rb_ensure(rb_yield, self, mSyslog_close, self);
189    }
190
191    return self;
192}
193
194/* call-seq:
195 *   reopen(ident, options, facility) => syslog
196 *
197 * :yields: syslog
198 *
199 * Closes and then reopens the syslog.
200 *
201 * Arguments are the same as for open().
202 */
203static VALUE mSyslog_reopen(int argc, VALUE *argv, VALUE self)
204{
205    mSyslog_close(self);
206
207    return mSyslog_open(argc, argv, self);
208}
209
210/* call-seq:
211 *   opened?
212 *
213 * Returns true if the syslog is open.
214 */
215static VALUE mSyslog_isopen(VALUE self)
216{
217    return syslog_opened ? Qtrue : Qfalse;
218}
219
220/* Returns the identity string used in the last call to open()
221 */
222static VALUE mSyslog_ident(VALUE self)
223{
224    return syslog_opened ? rb_str_new2(syslog_ident) : Qnil;
225}
226
227/* Returns the options bitmask used in the last call to open()
228 */
229static VALUE mSyslog_options(VALUE self)
230{
231    return syslog_opened ? INT2NUM(syslog_options) : Qnil;
232}
233
234/* Returns the facility number used in the last call to open()
235 */
236static VALUE mSyslog_facility(VALUE self)
237{
238    return syslog_opened ? INT2NUM(syslog_facility) : Qnil;
239}
240
241/* Returns the log priority mask in effect. The mask is not reset by opening
242 * or closing syslog.
243 */
244static VALUE mSyslog_get_mask(VALUE self)
245{
246    return syslog_opened ? INT2NUM(syslog_mask) : Qnil;
247}
248
249/* call-seq:
250 *   mask=(priority_mask)
251 *
252 * Sets the log priority mask. A method LOG_UPTO is defined to make it easier
253 * to set mask values. Example:
254 *
255 *   Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_ERR)
256 *
257 * Alternatively, specific priorities can be selected and added together using
258 * binary OR. Example:
259 *
260 *   Syslog.mask = Syslog::LOG_MASK(Syslog::LOG_ERR) | Syslog::LOG_MASK(Syslog::LOG_CRIT)
261 *
262 * The priority mask persists through calls to open() and close().
263 */
264static VALUE mSyslog_set_mask(VALUE self, VALUE mask)
265{
266    rb_secure(4);
267    if (!syslog_opened) {
268        rb_raise(rb_eRuntimeError, "must open syslog before setting log mask");
269    }
270
271    setlogmask(syslog_mask = NUM2INT(mask));
272
273    return mask;
274}
275
276/* call-seq:
277 *   log(priority, format_string, *format_args)
278 *
279 * Log a message with the specified priority. Example:
280 *
281 *   Syslog.log(Syslog::LOG_CRIT, "Out of disk space")
282 *   Syslog.log(Syslog::LOG_CRIT, "User %s logged in", ENV['USER'])
283 *
284 * The priority levels, in descending order, are:
285 *
286 * LOG_EMERG::   System is unusable
287 * LOG_ALERT::   Action needs to be taken immediately
288 * LOG_CRIT::    A critical condition has occurred
289 * LOG_ERR::     An error occurred
290 * LOG_WARNING:: Warning of a possible problem
291 * LOG_NOTICE::  A normal but significant condition occurred
292 * LOG_INFO::    Informational message
293 * LOG_DEBUG::   Debugging information
294 *
295 * Each priority level also has a shortcut method that logs with it's named priority.
296 * As an example, the two following statements would produce the same result:
297 *
298 *   Syslog.log(Syslog::LOG_ALERT, "Out of memory")
299 *   Syslog.alert("Out of memory")
300 *
301 * Format strings are as for printf/sprintf, except that in addition %m is
302 * replaced with the error message string that would be returned by
303 * strerror(errno).
304 *
305 */
306static VALUE mSyslog_log(int argc, VALUE *argv, VALUE self)
307{
308    VALUE pri;
309
310    if (argc < 2) {
311        rb_raise(rb_eArgError, "wrong number of arguments (%d for 2+)", argc);
312    }
313
314    argc--;
315    pri = *argv++;
316
317    if (!FIXNUM_P(pri)) {
318	rb_raise(rb_eTypeError, "type mismatch: %"PRIsVALUE" given", rb_obj_class(pri));
319    }
320
321    syslog_write(FIX2INT(pri), argc, argv);
322
323    return self;
324}
325
326/* Returns an inspect() string summarizing the object state.
327 */
328static VALUE mSyslog_inspect(VALUE self)
329{
330    Check_Type(self, T_MODULE);
331
332    if (!syslog_opened)
333	return rb_sprintf("<#%"PRIsVALUE": opened=false>", self);
334
335    return rb_sprintf("<#%"PRIsVALUE": opened=true, ident=\"%s\", options=%d, facility=%d, mask=%d>",
336		      self,
337		      syslog_ident,
338		      syslog_options,
339		      syslog_facility,
340		      syslog_mask);
341}
342
343/* Returns self, for backward compatibility.
344 */
345static VALUE mSyslog_instance(VALUE self)
346{
347    return self;
348}
349
350#define define_syslog_shortcut_method(pri, name) \
351static VALUE mSyslog_##name(int argc, VALUE *argv, VALUE self) \
352{ \
353    syslog_write((pri), argc, argv); \
354\
355    return self; \
356}
357
358#ifdef LOG_EMERG
359define_syslog_shortcut_method(LOG_EMERG, emerg)
360#endif
361#ifdef LOG_ALERT
362define_syslog_shortcut_method(LOG_ALERT, alert)
363#endif
364#ifdef LOG_CRIT
365define_syslog_shortcut_method(LOG_CRIT, crit)
366#endif
367#ifdef LOG_ERR
368define_syslog_shortcut_method(LOG_ERR, err)
369#endif
370#ifdef LOG_WARNING
371define_syslog_shortcut_method(LOG_WARNING, warning)
372#endif
373#ifdef LOG_NOTICE
374define_syslog_shortcut_method(LOG_NOTICE, notice)
375#endif
376#ifdef LOG_INFO
377define_syslog_shortcut_method(LOG_INFO, info)
378#endif
379#ifdef LOG_DEBUG
380define_syslog_shortcut_method(LOG_DEBUG, debug)
381#endif
382
383/* call-seq:
384 *   LOG_MASK(priority_level) => priority_mask
385 *
386 * Generates a mask bit for a priority level. See #mask=
387 */
388static VALUE mSyslogMacros_LOG_MASK(VALUE mod, VALUE pri)
389{
390    return INT2FIX(LOG_MASK(NUM2INT(pri)));
391}
392
393/* call-seq:
394 *   LOG_UPTO(priority_level) => priority_mask
395 *
396 * Generates a mask value for priority levels at or below the level specified.
397 * See #mask=
398 */
399static VALUE mSyslogMacros_LOG_UPTO(VALUE mod, VALUE pri)
400{
401    return INT2FIX(LOG_UPTO(NUM2INT(pri)));
402}
403
404static VALUE mSyslogMacros_included(VALUE mod, VALUE target)
405{
406    rb_extend_object(target, mSyslogMacros);
407    return mod;
408}
409
410/* The syslog package provides a Ruby interface to the POSIX system logging
411 * facility.
412 *
413 * Syslog messages are typically passed to a central logging daemon.
414 * The daemon may filter them; route them into different files (usually
415 * found under /var/log); place them in SQL databases; forward
416 * them to centralized logging servers via TCP or UDP; or even alert the
417 * system administrator via email, pager or text message.
418 *
419 * Unlike application-level logging via Logger or Log4r, syslog is designed
420 * to allow secure tamper-proof logging.
421 *
422 * The syslog protocol is standardized in RFC 5424.
423 */
424void Init_syslog()
425{
426    mSyslog = rb_define_module("Syslog");
427
428    mSyslogConstants    = rb_define_module_under(mSyslog, "Constants");
429
430    mSyslogOption       = rb_define_module_under(mSyslog, "Option");
431    mSyslogFacility     = rb_define_module_under(mSyslog, "Facility");
432    mSyslogLevel        = rb_define_module_under(mSyslog, "Level");
433    mSyslogMacros       = rb_define_module_under(mSyslog, "Macros");
434
435    rb_define_module_function(mSyslog, "open", mSyslog_open, -1);
436    rb_define_module_function(mSyslog, "reopen", mSyslog_reopen, -1);
437    rb_define_module_function(mSyslog, "open!", mSyslog_reopen, -1);
438    rb_define_module_function(mSyslog, "opened?", mSyslog_isopen, 0);
439
440    rb_define_module_function(mSyslog, "ident", mSyslog_ident, 0);
441    rb_define_module_function(mSyslog, "options", mSyslog_options, 0);
442    rb_define_module_function(mSyslog, "facility", mSyslog_facility, 0);
443
444    rb_define_module_function(mSyslog, "log", mSyslog_log, -1);
445    rb_define_module_function(mSyslog, "close", mSyslog_close, 0);
446    rb_define_module_function(mSyslog, "mask", mSyslog_get_mask, 0);
447    rb_define_module_function(mSyslog, "mask=", mSyslog_set_mask, 1);
448
449    rb_define_module_function(mSyslog, "inspect", mSyslog_inspect, 0);
450    rb_define_module_function(mSyslog, "instance", mSyslog_instance, 0);
451
452    /* Syslog options */
453
454#define rb_define_syslog_option(c) \
455    rb_define_const(mSyslogOption, #c, INT2NUM(c))
456
457#ifdef LOG_PID
458    rb_define_syslog_option(LOG_PID);
459#endif
460#ifdef LOG_CONS
461    rb_define_syslog_option(LOG_CONS);
462#endif
463#ifdef LOG_ODELAY
464    rb_define_syslog_option(LOG_ODELAY); /* deprecated */
465#endif
466#ifdef LOG_NDELAY
467    rb_define_syslog_option(LOG_NDELAY);
468#endif
469#ifdef LOG_NOWAIT
470    rb_define_syslog_option(LOG_NOWAIT); /* deprecated */
471#endif
472#ifdef LOG_PERROR
473    rb_define_syslog_option(LOG_PERROR);
474#endif
475
476    /* Syslog facilities */
477
478#define rb_define_syslog_facility(c) \
479    rb_define_const(mSyslogFacility, #c, INT2NUM(c))
480
481#ifdef LOG_AUTH
482    rb_define_syslog_facility(LOG_AUTH);
483#endif
484#ifdef LOG_AUTHPRIV
485    rb_define_syslog_facility(LOG_AUTHPRIV);
486#endif
487#ifdef LOG_CONSOLE
488    rb_define_syslog_facility(LOG_CONSOLE);
489#endif
490#ifdef LOG_CRON
491    rb_define_syslog_facility(LOG_CRON);
492#endif
493#ifdef LOG_DAEMON
494    rb_define_syslog_facility(LOG_DAEMON);
495#endif
496#ifdef LOG_FTP
497    rb_define_syslog_facility(LOG_FTP);
498#endif
499#ifdef LOG_KERN
500    rb_define_syslog_facility(LOG_KERN);
501#endif
502#ifdef LOG_LPR
503    rb_define_syslog_facility(LOG_LPR);
504#endif
505#ifdef LOG_MAIL
506    rb_define_syslog_facility(LOG_MAIL);
507#endif
508#ifdef LOG_NEWS
509    rb_define_syslog_facility(LOG_NEWS);
510#endif
511#ifdef LOG_NTP
512   rb_define_syslog_facility(LOG_NTP);
513#endif
514#ifdef LOG_SECURITY
515    rb_define_syslog_facility(LOG_SECURITY);
516#endif
517#ifdef LOG_SYSLOG
518    rb_define_syslog_facility(LOG_SYSLOG);
519#endif
520#ifdef LOG_USER
521    rb_define_syslog_facility(LOG_USER);
522#endif
523#ifdef LOG_UUCP
524    rb_define_syslog_facility(LOG_UUCP);
525#endif
526#ifdef LOG_LOCAL0
527    rb_define_syslog_facility(LOG_LOCAL0);
528#endif
529#ifdef LOG_LOCAL1
530    rb_define_syslog_facility(LOG_LOCAL1);
531#endif
532#ifdef LOG_LOCAL2
533    rb_define_syslog_facility(LOG_LOCAL2);
534#endif
535#ifdef LOG_LOCAL3
536    rb_define_syslog_facility(LOG_LOCAL3);
537#endif
538#ifdef LOG_LOCAL4
539    rb_define_syslog_facility(LOG_LOCAL4);
540#endif
541#ifdef LOG_LOCAL5
542    rb_define_syslog_facility(LOG_LOCAL5);
543#endif
544#ifdef LOG_LOCAL6
545    rb_define_syslog_facility(LOG_LOCAL6);
546#endif
547#ifdef LOG_LOCAL7
548    rb_define_syslog_facility(LOG_LOCAL7);
549#endif
550
551    /* Syslog levels and the shortcut methods */
552
553#define rb_define_syslog_level(c, m)				\
554    rb_define_const(mSyslogLevel, #c, INT2NUM(c));		\
555    rb_define_module_function(mSyslog, #m, mSyslog_##m, -1)
556
557#ifdef LOG_EMERG
558    rb_define_syslog_level(LOG_EMERG, emerg);
559#endif
560#ifdef LOG_ALERT
561    rb_define_syslog_level(LOG_ALERT, alert);
562#endif
563#ifdef LOG_CRIT
564    rb_define_syslog_level(LOG_CRIT, crit);
565#endif
566#ifdef LOG_ERR
567    rb_define_syslog_level(LOG_ERR, err);
568#endif
569#ifdef LOG_WARNING
570    rb_define_syslog_level(LOG_WARNING, warning);
571#endif
572#ifdef LOG_NOTICE
573    rb_define_syslog_level(LOG_NOTICE, notice);
574#endif
575#ifdef LOG_INFO
576    rb_define_syslog_level(LOG_INFO, info);
577#endif
578#ifdef LOG_DEBUG
579    rb_define_syslog_level(LOG_DEBUG, debug);
580#endif
581
582    /* Syslog macros */
583
584    rb_define_method(mSyslogMacros, "LOG_MASK", mSyslogMacros_LOG_MASK, 1);
585    rb_define_method(mSyslogMacros, "LOG_UPTO", mSyslogMacros_LOG_UPTO, 1);
586    rb_define_singleton_method(mSyslogMacros, "included", mSyslogMacros_included, 1);
587
588    rb_include_module(mSyslogConstants, mSyslogOption);
589    rb_include_module(mSyslogConstants, mSyslogFacility);
590    rb_include_module(mSyslogConstants, mSyslogLevel);
591    rb_funcall(mSyslogConstants, rb_intern("include"), 1, mSyslogMacros);
592
593    rb_define_singleton_method(mSyslogConstants, "included", mSyslogMacros_included, 1);
594    rb_funcall(mSyslog, rb_intern("include"), 1, mSyslogConstants);
595}
596