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