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