]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/syslog.cc
* Merge in cygwin-64bit-branch.
[newlib-cygwin.git] / winsup / cygwin / syslog.cc
CommitLineData
1fd5e000
CF
1/* syslog.cc
2
bc837d22
CF
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007, 2008, 2009, 2011, 2012 Red Hat, Inc.
1fd5e000
CF
5
6This file is part of Cygwin.
7
8This software is a copyrighted work licensed under the terms of the
9Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10details. */
11
9d61c9a2 12#define __INSIDE_CYGWIN_NET__
db80f635 13#define USE_SYS_TYPES_FD_SET
4c8d72de 14#include "winsup.h"
db80f635
CV
15#include <ws2tcpip.h>
16#include <iphlpapi.h>
1fd5e000
CF
17#include <stdlib.h>
18#include <stdio.h>
19#include <syslog.h>
1fd5e000 20#include <unistd.h>
8584a9df 21#include <sys/un.h>
169c465a 22#include "cygerrno.h"
6b91b8d5 23#include "security.h"
47063f00 24#include "path.h"
7ac61736 25#include "fhandler.h"
e2ebe117 26#include "dtable.h"
1126c2b0 27#include "cygheap.h"
29d52c8a 28#include "cygtls.h"
39735c85 29#include "tls_pbuf.h"
1fd5e000 30
da008633 31#define CYGWIN_LOG_NAME L"Cygwin"
1fd5e000 32
a0307f99
CV
33static struct
34{
35 wchar_t *process_ident;
36 int process_logopt;
37 int process_facility;
38 int process_logmask;
39} syslog_globals = { NULL, 0, 0, LOG_UPTO (LOG_DEBUG) };
40
14e06cca 41/* openlog: save the passed args. Don't open the system log or /dev/log yet. */
c367dfd0 42extern "C" void
1fd5e000
CF
43openlog (const char *ident, int logopt, int facility)
44{
a0307f99
CV
45 wchar_t *new_ident = NULL;
46
1fd5e000 47 debug_printf ("openlog called with (%s, %d, %d)",
61522196 48 ident ? ident : "<NULL>", logopt, facility);
1fd5e000 49
1fd5e000
CF
50 if (ident)
51 {
a0307f99
CV
52 sys_mbstowcs_alloc (&new_ident, HEAP_NOTHEAP, ident);
53 if (!new_ident)
da008633 54 debug_printf ("failed to allocate memory for "
a0307f99
CV
55 "syslog_globals.process_ident");
56 else
57 {
58 wchar_t *old_ident = syslog_globals.process_ident;
59 syslog_globals.process_ident = new_ident;
60 if (old_ident)
61 free (old_ident);
1fd5e000 62 }
1fd5e000 63 }
a0307f99
CV
64 syslog_globals.process_logopt = logopt;
65 syslog_globals.process_facility = facility;
1fd5e000
CF
66}
67
68/* setlogmask: set the log priority mask and return previous mask.
69 If maskpri is zero, just return previous. */
1fd5e000
CF
70int
71setlogmask (int maskpri)
72{
73 if (maskpri == 0)
a0307f99 74 return syslog_globals.process_logmask;
1fd5e000 75
a0307f99
CV
76 int old_mask = syslog_globals.process_logmask;
77 syslog_globals.process_logmask = maskpri;
1fd5e000
CF
78
79 return old_mask;
80}
1fd5e000 81
29d52c8a
CF
82/* Private class used to handle formatting of syslog message
83 It is named pass_handler because it does a two-pass handling of log
1fd5e000
CF
84 strings. The first pass counts the length of the string, and the second
85 one builds the string. */
86
87class pass_handler
88{
89 private:
90 FILE *fp_;
91 char *message_;
92 int total_len_;
93
94 void shutdown ();
95
96 /* Explicitly disallow copies */
97 pass_handler (const pass_handler &);
98 pass_handler & operator = (const pass_handler &);
99
100 public:
101 pass_handler ();
102 ~pass_handler ();
103
104 int initialize (int);
105
106 int print (const char *,...);
107 int print_va (const char *, va_list);
108 char *get_message () const { return message_; }
a3cfd73a 109 void set_message (char *s) { message_ = s; *message_ = '\0'; }
1fd5e000
CF
110};
111
112pass_handler::pass_handler () : fp_ (0), message_ (0), total_len_ (0)
113{
114 ;
115}
116
117pass_handler::~pass_handler ()
118{
119 shutdown ();
120}
121
122void
123pass_handler::shutdown ()
124{
7b972f5d 125 if (fp_ != NULL)
1fd5e000
CF
126 {
127 fclose (fp_);
128 fp_ = 0;
129 }
1fd5e000
CF
130}
131
132int
133pass_handler::initialize (int pass_number)
134{
135 shutdown ();
7b972f5d
CF
136 if (pass_number)
137 return total_len_ + 1;
138
139 fp_ = fopen ("/dev/null", "wb");
fe836470 140 setbuf (fp_, NULL);
7b972f5d 141 if (fp_ == NULL)
1fd5e000 142 {
7b972f5d
CF
143 debug_printf ("failed to open /dev/null");
144 return -1;
1fd5e000 145 }
7b972f5d 146 total_len_ = 0;
1fd5e000
CF
147 return 0;
148}
149
150int
151pass_handler::print (const char *fmt, ...)
152{
153 va_list ap;
154 va_start (ap, fmt);
155 int ret = print_va (fmt, ap);
156 va_end (ap);
157 return ret;
158}
159
160int
161pass_handler::print_va (const char *fmt, va_list list)
162{
a3cfd73a 163 if (fp_ != NULL)
1fd5e000
CF
164 {
165 int len = vfprintf (fp_, fmt, list);
166 if (len < 0)
7b972f5d
CF
167 return -1;
168 total_len_ += len;
169 return 0;
1fd5e000 170 }
a3cfd73a 171 else if (message_ != NULL)
1fd5e000
CF
172 {
173 char *printpos = &message_[strlen (message_)];
174 vsprintf (printpos, fmt, list);
175 return 0;
176 }
177 debug_printf ("FAILURE ! fp_ and message_ both 0!! ");
178 return -1;
179}
180
9d61c9a2 181static NO_COPY muto try_connect_guard;
8584a9df
CV
182static enum {
183 not_inited,
184 inited_failed,
185 inited_dgram,
186 inited_stream
187} syslogd_inited;
9d61c9a2
CV
188static int syslogd_sock = -1;
189extern "C" int cygwin_socket (int, int, int);
190extern "C" int cygwin_connect (int, const struct sockaddr *, int);
39735c85
CV
191extern int get_inet_addr (const struct sockaddr *, int,
192 struct sockaddr_storage *, int *,
193 int * = NULL, int * = NULL);
9d61c9a2 194
8584a9df
CV
195static void
196connect_syslogd ()
9d61c9a2 197{
8584a9df
CV
198 int fd;
199 struct sockaddr_un sun;
39735c85
CV
200 struct sockaddr_storage sst;
201 int len, type;
8584a9df
CV
202
203 if (syslogd_inited != not_inited && syslogd_sock >= 0)
204 close (syslogd_sock);
205 syslogd_inited = inited_failed;
206 syslogd_sock = -1;
8584a9df
CV
207 sun.sun_family = AF_LOCAL;
208 strncpy (sun.sun_path, _PATH_LOG, sizeof sun.sun_path);
933d2af5 209 if (get_inet_addr ((struct sockaddr *) &sun, sizeof sun, &sst, &len, &type))
39735c85
CV
210 return;
211 if ((fd = cygwin_socket (AF_LOCAL, type, 0)) < 0)
212 return;
213 if (cygwin_connect (fd, (struct sockaddr *) &sun, sizeof sun) == 0)
9d61c9a2 214 {
39735c85
CV
215 /* connect on a dgram socket always succeeds. We still don't know
216 if syslogd is actually listening. */
217 if (type == SOCK_DGRAM)
05726ddd 218 {
39735c85
CV
219 tmp_pathbuf tp;
220 PMIB_UDPTABLE tab = (PMIB_UDPTABLE) tp.w_get ();
221 DWORD size = 65536;
222 bool found = false;
223 struct sockaddr_in *sa = (struct sockaddr_in *) &sst;
224
225 if (GetUdpTable (tab, &size, FALSE) == NO_ERROR)
226 {
227 for (DWORD i = 0; i < tab->dwNumEntries; ++i)
228 if (tab->table[i].dwLocalAddr == sa->sin_addr.s_addr
229 && tab->table[i].dwLocalPort == sa->sin_port)
230 {
231 found = true;
232 break;
233 }
234 if (!found)
235 {
236 /* No syslogd is listening. */
237 close (fd);
238 return;
239 }
240 }
8584a9df 241 }
39735c85 242 syslogd_inited = type == SOCK_DGRAM ? inited_dgram : inited_stream;
9d61c9a2 243 }
8584a9df 244 syslogd_sock = fd;
fabfb1a1 245 fcntl64 (syslogd_sock, F_SETFD, FD_CLOEXEC);
39735c85
CV
246 debug_printf ("found /dev/log, fd = %d, type = %s",
247 fd, syslogd_inited == inited_stream ? "STREAM" : "DGRAM");
8584a9df
CV
248 return;
249}
250
251static int
61522196 252try_connect_syslogd (int priority, const char *msg, size_t len)
8584a9df 253{
bf2d5da3 254 ssize_t ret = -1;
8584a9df
CV
255
256 try_connect_guard.init ("try_connect_guard")->acquire ();
257 if (syslogd_inited == not_inited)
258 connect_syslogd ();
39735c85 259 if (syslogd_inited != inited_failed)
9d61c9a2 260 {
bf2d5da3
CV
261 char pribuf[16];
262 sprintf (pribuf, "<%d>", priority);
263 struct iovec iv[2] =
264 {
de935f6d 265 { pribuf, strlen (pribuf) },
bf2d5da3
CV
266 { (char *) msg, len }
267 };
268
bf2d5da3 269 ret = writev (syslogd_sock, iv, 2);
8584a9df 270 /* If the syslog daemon has been restarted and /dev/log was
34f5d087 271 a stream socket, the connection is broken. In this case,
8584a9df
CV
272 try to reopen the socket and try again. */
273 if (ret < 0 && syslogd_inited == inited_stream)
274 {
275 connect_syslogd ();
276 if (syslogd_sock >= 0)
277 ret = writev (syslogd_sock, iv, 2);
278 }
9d61c9a2 279 /* If write fails and LOG_CONS is set, return failure to vsyslog so
05726ddd 280 it falls back to the usual logging method for this OS. */
a0307f99 281 if (ret >= 0 || !(syslog_globals.process_logopt & LOG_CONS))
05726ddd 282 ret = syslogd_sock;
9d61c9a2 283 }
9d61c9a2 284 try_connect_guard.release ();
9d61c9a2
CV
285 return ret;
286}
287
14e06cca
CV
288/* syslog: creates the log message and writes to /dev/log, or to the
289 NT system log if /dev/log isn't available.
290
291 FIXME. WinNT system log messages don't look pretty, but in order to
292 fix this we have to embed resources in the code and tell the NT
293 registry where we are, blech (what happens if we move ?). We could,
294 however, add the resources in Cygwin and always point to that. */
1fd5e000 295
c367dfd0 296extern "C" void
54152c7e 297vsyslog (int priority, const char *message, va_list ap)
1fd5e000 298{
61522196 299 debug_printf ("%y %s", priority, message);
eef57fe1 300 /* If the priority fails the current mask, reject */
a0307f99 301 if ((LOG_MASK (LOG_PRI (priority)) & syslog_globals.process_logmask) == 0)
eef57fe1 302 {
61522196 303 debug_printf ("failing message %y due to priority mask %y",
a0307f99 304 priority, syslog_globals.process_logmask);
eef57fe1
CV
305 return;
306 }
bf2d5da3 307
2bc3381e 308 /* Set default facility to LOG_USER if not yet set via openlog. */
a0307f99
CV
309 if (!syslog_globals.process_facility)
310 syslog_globals.process_facility = LOG_USER;
2bc3381e 311
eef57fe1
CV
312 /* Add default facility if not in the given priority. */
313 if (!(priority & LOG_FACMASK))
a0307f99 314 priority |= syslog_globals.process_facility;
1fd5e000 315
eef57fe1
CV
316 /* Translate %m in the message to error text */
317 char *errtext = strerror (get_errno ());
318 int errlen = strlen (errtext);
319 int numfound = 0;
1fd5e000 320
eef57fe1
CV
321 for (const char *cp = message; *cp; cp++)
322 if (*cp == '%' && cp[1] == 'm')
323 numfound++;
1fd5e000 324
eef57fe1
CV
325 char *newmessage = (char *) alloca (strlen (message) +
326 (errlen * numfound) + 1);
1fd5e000 327
eef57fe1
CV
328 if (newmessage == NULL)
329 {
330 debug_printf ("failed to allocate newmessage");
331 return;
332 }
1fd5e000 333
eef57fe1
CV
334 char *dst = newmessage;
335 for (const char *cp2 = message; *cp2; cp2++)
336 if (*cp2 == '%' && cp2[1] == 'm')
1fd5e000 337 {
eef57fe1
CV
338 cp2++;
339 strcpy (dst, errtext);
340 while (*dst)
341 dst++;
1fd5e000 342 }
eef57fe1
CV
343 else
344 *dst++ = *cp2;
1fd5e000 345
eef57fe1
CV
346 *dst = '\0';
347 message = newmessage;
1fd5e000 348
eef57fe1
CV
349 /* Work out the priority type - we ignore the facility for now.. */
350 WORD eventType;
351 switch (LOG_PRI (priority))
352 {
353 case LOG_EMERG:
354 case LOG_ALERT:
355 case LOG_CRIT:
356 case LOG_ERR:
357 eventType = EVENTLOG_ERROR_TYPE;
358 break;
359 case LOG_WARNING:
360 eventType = EVENTLOG_WARNING_TYPE;
361 break;
362 case LOG_NOTICE:
363 case LOG_INFO:
364 case LOG_DEBUG:
365 eventType = EVENTLOG_INFORMATION_TYPE;
366 break;
367 default:
368 eventType = EVENTLOG_ERROR_TYPE;
369 break;
370 }
1fd5e000 371
eef57fe1
CV
372 /* We need to know how long the buffer needs to be.
373 The only legal way I can see of doing this is to
374 do a vfprintf to /dev/null, and count the bytes
375 output, then do it again to a malloc'ed string. This
376 is ugly, slow, but prevents core dumps :-).
377 */
378 pass_handler pass;
379 for (int pass_number = 0; pass_number < 2; ++pass_number)
380 {
381 int n = pass.initialize (pass_number);
382 if (n == -1)
383 return;
384 else if (n > 0)
385 pass.set_message ((char *) alloca (n));
1fd5e000 386
eef57fe1 387 /* Deal with ident_string */
a0307f99 388 if (syslog_globals.process_ident != NULL)
eef57fe1 389 {
a0307f99 390 if (pass.print ("%ls: ", syslog_globals.process_ident) == -1)
eef57fe1
CV
391 return;
392 }
a0307f99 393 if (syslog_globals.process_logopt & LOG_PID)
eef57fe1
CV
394 {
395 if (pass.print ("PID %u: ", getpid ()) == -1)
396 return;
397 }
1fd5e000 398
eef57fe1
CV
399 /* Print out the variable part */
400 if (pass.print_va (message, ap) == -1)
401 return;
1fd5e000 402
eef57fe1 403 }
eef57fe1 404 char *total_msg = pass.get_message ();
61522196 405 size_t len = strlen (total_msg);
eef57fe1
CV
406 if (len != 0 && (total_msg[len - 1] == '\n'))
407 total_msg[--len] = '\0';
1fd5e000 408
a0307f99 409 if (syslog_globals.process_logopt & LOG_PERROR)
eef57fe1
CV
410 {
411 write (STDERR_FILENO, total_msg, len);
412 write (STDERR_FILENO, "\n", 1);
413 }
414
415 int fd;
416 if ((fd = try_connect_syslogd (priority, total_msg, len + 1)) < 0)
417 {
418 /* If syslogd isn't present, open the event log and send the message */
da008633
CV
419 HANDLE hEventSrc;
420
a0307f99 421 hEventSrc = RegisterEventSourceW (NULL, syslog_globals.process_ident
da008633
CV
422 ?: CYGWIN_LOG_NAME);
423 if (!hEventSrc)
424 debug_printf ("RegisterEventSourceW, %E");
425 else
eef57fe1 426 {
da008633
CV
427 wchar_t *msg_strings[1];
428 tmp_pathbuf tp;
429 msg_strings[0] = tp.w_get ();
430 sys_mbstowcs (msg_strings[0], NT_MAX_PATH, total_msg);
431 if (!ReportEventW (hEventSrc, eventType, 0, 0, cygheap->user.sid (),
432 1, 0, (const wchar_t **) msg_strings, NULL))
433 debug_printf ("ReportEventW, %E");
434 DeregisterEventSource (hEventSrc);
eef57fe1 435 }
eef57fe1 436 }
1fd5e000
CF
437}
438
54152c7e
CV
439extern "C" void
440syslog (int priority, const char *message, ...)
441{
442 va_list ap;
443 va_start (ap, message);
444 vsyslog (priority, message, ap);
445 va_end (ap);
446}
447
13505ca8
CV
448static NO_COPY muto klog_guard;
449fhandler_mailslot *dev_kmsg;
450
451extern "C" void
452vklog (int priority, const char *message, va_list ap)
453{
454 /* TODO: kernel messages are under our control entirely and they should
455 be quick. No playing with /dev/null, but a fixed upper size for now. */
456 char buf[2060]; /* 2048 + a prority */
9e68bb7b
CV
457 if (!(priority & ~LOG_PRIMASK))
458 priority = LOG_KERN | LOG_PRI (priority);
13505ca8
CV
459 __small_sprintf (buf, "<%d>", priority);
460 __small_vsprintf (buf + strlen (buf), message, ap);
461 klog_guard.init ("klog_guard")->acquire ();
462 if (!dev_kmsg)
463 dev_kmsg = (fhandler_mailslot *) build_fh_name ("/dev/kmsg");
464 if (dev_kmsg && !dev_kmsg->get_handle ())
465 dev_kmsg->open (O_WRONLY, 0);
466 if (dev_kmsg && dev_kmsg->get_handle ())
467 dev_kmsg->write (buf, strlen (buf) + 1);
13505ca8
CV
468 klog_guard.release ();
469}
470
471extern "C" void
472klog (int priority, const char *message, ...)
473{
474 va_list ap;
475 va_start (ap, message);
476 vklog (priority, message, ap);
477 va_end (ap);
478}
479
c367dfd0 480extern "C" void
1fd5e000
CF
481closelog (void)
482{
9d61c9a2 483 try_connect_guard.init ("try_connect_guard")->acquire ();
8584a9df 484 if (syslogd_inited != not_inited && syslogd_sock >= 0)
9d61c9a2
CV
485 {
486 close (syslogd_sock);
487 syslogd_sock = -1;
8584a9df 488 syslogd_inited = not_inited;
9d61c9a2
CV
489 }
490 try_connect_guard.release ();
1fd5e000 491}
This page took 0.472029 seconds and 5 git commands to generate.