1 (* syslog(3) routines for ocaml
2 This library is based on Shawn Wagner's original syslog
3 library as included in annexlib, with significant modifications
4 by by Eric Stokes <eric.stokes@csun.edu>.
6 Copyright (C) 2002 Shawn Wagner <raevnos@pennmush.org>
7 Copyright (C) 2005 Eric Stokes <eric.stokes@csun.edu>
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 (** The assorted logging facilities. The default is [`LOG_USER]. You
27 can set a new default with openlog, or give a specific facility per
30 [ `LOG_KERN
| `LOG_USER
| `LOG_MAIL
| `LOG_DAEMON
| `LOG_AUTH
31 | `LOG_SYSLOG
| `LOG_LPR
| `LOG_NEWS
| `LOG_UUCP
| `LOG_CRON
32 | `LOG_AUTHPRIV
| `LOG_FTP
| `LOG_NTP
| `LOG_SECURITY
33 | `LOG_CONSOLE
| `LOG_LOCAL0
| `LOG_LOCAL1
| `LOG_LOCAL2
34 | `LOG_LOCAL3
| `LOG_LOCAL4
| `LOG_LOCAL5
| `LOG_LOCAL6
37 (** Flags to pass to openlog. [`LOG_CONS] isn't implemented yet. *)
38 type flag
= [ `LOG_CONS
| `LOG_PERROR
| `LOG_PID
]
40 (** The priority of the error. *)
41 type level
= [ `LOG_EMERG
| `LOG_ALERT
| `LOG_CRIT
| `LOG_ERR
| `LOG_WARNING
42 | `LOG_NOTICE
| `LOG_INFO
| `LOG_DEBUG
]
44 exception Syslog_error
of string
46 let facility_of_string s
=
47 match String.lowercase s
with
51 | "daemon" -> `LOG_DAEMON
53 | "syslog" -> `LOG_SYSLOG
58 | "authpriv" -> `LOG_AUTHPRIV
61 | "security" -> `LOG_SECURITY
62 | "console" -> `LOG_CONSOLE
63 | "local0" -> `LOG_LOCAL0
64 | "local1" -> `LOG_LOCAL1
65 | "local2" -> `LOG_LOCAL2
66 | "local3" -> `LOG_LOCAL3
67 | "local4" -> `LOG_LOCAL4
68 | "local5" -> `LOG_LOCAL5
69 | "local6" -> `LOG_LOCAL6
70 | "local7" -> `LOG_LOCAL7
73 ("facility_of_string: invalid facility, " ^
76 let facility_to_num fac
=
77 Int32.of_int
(match fac
with
78 | `LOG_KERN
-> 0 lsl 3
79 | `LOG_USER
-> 1 lsl 3
80 | `LOG_MAIL
-> 2 lsl 3
81 | `LOG_DAEMON
-> 3 lsl 3
82 | `LOG_AUTH
-> 4 lsl 3
83 | `LOG_SYSLOG
-> 5 lsl 3
85 | `LOG_NEWS
-> 7 lsl 3
86 | `LOG_UUCP
-> 8 lsl 3
87 | `LOG_CRON
-> 9 lsl 3
88 | `LOG_AUTHPRIV
-> 10 lsl 3
89 | `LOG_FTP
-> 11 lsl 3
90 | `LOG_NTP
-> 12 lsl 3
91 | `LOG_SECURITY
-> 13 lsl 3
92 | `LOG_CONSOLE
-> 14 lsl 3
93 | `LOG_LOCAL0
-> 16 lsl 3
94 | `LOG_LOCAL1
-> 17 lsl 3
95 | `LOG_LOCAL2
-> 18 lsl 3
96 | `LOG_LOCAL3
-> 19 lsl 3
97 | `LOG_LOCAL4
-> 20 lsl 3
98 | `LOG_LOCAL5
-> 21 lsl 3
99 | `LOG_LOCAL6
-> 22 lsl 3
100 | `LOG_LOCAL7
-> 23 lsl 3)
102 let level_to_num lev
=
103 Int32.of_int
(match lev
with
114 let level_mask = 0x07
115 let facility_mask = 0x03f8
118 mutable fd
: Unix.file_descr
;
119 mutable connected
: bool;
120 mutable flags
: flag list
;
123 mutable logpath
: string;
126 let open_connection loginfo
=
127 let module U
= Unix.LargeFile
in
128 match loginfo
.logpath
with
129 "" -> raise
(Syslog_error
"unable to find the syslog socket or pipe, is syslogd running?")
131 (match (U.stat logpath
).U.st_kind
with
133 let logaddr = Unix.ADDR_UNIX logpath
in
135 loginfo
.fd
<- Unix.socket
Unix.PF_UNIX SOCK_DGRAM
0;
136 Unix.connect loginfo
.fd
logaddr
137 with Unix.Unix_error
(Unix.EPROTOTYPE
, _
, _
) ->
138 (* try again with a stream socket for syslog-ng *)
139 loginfo
.fd
<- Unix.socket
Unix.PF_UNIX SOCK_STREAM
0;
140 Unix.connect loginfo
.fd
logaddr);
141 loginfo
.connected
<- true;
143 loginfo
.fd
<- Unix.openfile logpath
[Unix.O_WRONLY
] 0o666
;
144 loginfo
.connected
<- true;
145 | _
-> raise
(Syslog_error
"invalid log path, not a socket or pipe"))
148 ?
(logpath
=(try ignore
(Unix.stat
"/dev/log");"/dev/log"
149 with Unix.Unix_error
(Unix.ENOENT
, _
, _
) ->
150 (try ignore
(Unix.stat
"/var/run/syslog");"/var/run/syslog"
151 with Unix.Unix_error
(Unix.ENOENT
, _
, _
) -> "")))
152 ?
(facility
=`LOG_USER
)
155 let loginfo = {fd
= Unix.stderr
;
159 fac
= facility_to_num facility
;
162 open_connection loginfo;
165 let log_console _msg
= ()
167 let ascdate {tm_sec
=sec
;tm_min
=min
;tm_hour
=hour
;
168 tm_mday
=mday
;tm_mon
=mon
;tm_year
=_
;
169 tm_wday
=_
;tm_yday
=_
;tm_isdst
=_
} =
184 | _
-> raise
(Syslog_error
"invalid month")
186 (Printf.sprintf
"%s %02d %02d:%02d:%02d" asc_mon mday hour min sec
)
188 let protected_write loginfo str
=
190 (try close
loginfo.fd
with _
-> ());
191 loginfo.connected
<- false;
192 (try open_connection loginfo with _
-> ());
193 if List.mem `LOG_CONS
loginfo.flags
then log_console str
195 let prev = Sys.signal
Sys.sigpipe
(Sys.Signal_handle
fallback) in
197 ignore
(write_substring
loginfo.fd str
0 (String.length str
));
198 Sys.set_signal
Sys.sigpipe
prev
199 with Unix_error
(_
, _
, _
) ->
200 (* on error, attempt to reconnect *)
202 Sys.set_signal
Sys.sigpipe
prev
204 let syslog ?fac
loginfo lev str
=
205 let msg = Buffer.create
64 in
206 let realfac = match fac
with
207 | Some f
-> facility_to_num f
208 | None
-> loginfo.fac
in
209 let levfac = Int32.logor
realfac (level_to_num lev
)
210 and now
= ascdate (localtime
(Unix.time
())) in
211 Printf.bprintf
msg "<%ld>%.15s " levfac now
;
212 let len1 = Buffer.length
msg
213 and len2
= String.length
loginfo.tag
in
214 if len1 + len2
< 64 then
215 Buffer.add_string
msg loginfo.tag
217 Buffer.add_substring
msg loginfo.tag
0 (64 - len1);
218 if List.mem `LOG_PID
loginfo.flags
then
219 Printf.bprintf
msg "[%d]" (Unix.getpid
());
220 if String.length
loginfo.tag
> 0 then
221 Buffer.add_string
msg ": ";
222 Buffer.add_string
msg str
;
223 let realmsg = ref (Buffer.contents
msg) in
224 if String.length
!realmsg > 1024 then begin
225 realmsg := String.sub
!realmsg 0 1012 ^
"<truncated>"
227 protected_write loginfo !realmsg;
228 if List.mem `LOG_PERROR
loginfo.flags
then begin
230 ignore
(Unix.write_substring
Unix.stderr
!realmsg 0 (String.length
!realmsg));
231 ignore
(Unix.write_substring
Unix.stderr
"\n" 0 1)
235 let closelog loginfo =
236 if loginfo.connected
then
238 Unix.close
loginfo.fd
;
239 loginfo.connected
<- false
243 loginfo.fac
<- facility_to_num `LOG_USER