syslog.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. syslog.c
  9. Abstract:
  10. This module implements system logging support for the C library.
  11. Author:
  12. Evan Green 22-Jan-2015
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "libcp.h"
  20. #include <assert.h>
  21. #include <errno.h>
  22. #include <fcntl.h>
  23. #include <syslog.h>
  24. #include <sys/un.h>
  25. #include <stdlib.h>
  26. #include <unistd.h>
  27. //
  28. // ---------------------------------------------------------------- Definitions
  29. //
  30. //
  31. // Define the path of the Unix socket the C library sends log messages to.
  32. // A logging daemon is expected to be a socket server on the other end there.
  33. //
  34. #define SYSLOG_PATH "/dev/log"
  35. //
  36. // Define the console path syslog writes to if normal writes failed.
  37. //
  38. #define SYSLOG_CONSOLE_PATH "/dev/console"
  39. //
  40. // Define the maximum size of a syslog message.
  41. //
  42. #define SYSLOG_MESSAGE_MAX 2048
  43. #define SYSLOG_MESSAGE_HEADER_MAX 130
  44. #define SYSLOG_TIME_FORMAT "%h %e %T"
  45. #define SYSLOG_TIME_BUFFER_SIZE 20
  46. //
  47. // ------------------------------------------------------ Data Type Definitions
  48. //
  49. //
  50. // ----------------------------------------------- Internal Function Prototypes
  51. //
  52. VOID
  53. ClpOpenLog (
  54. int Options,
  55. int Facility,
  56. BOOL MustConnect
  57. );
  58. VOID
  59. ClpCloseLog (
  60. VOID
  61. );
  62. VOID
  63. ClpSyslogWrite (
  64. int FileDescriptor,
  65. const void *Buffer,
  66. size_t ByteCount
  67. );
  68. //
  69. // -------------------------------------------------------------------- Globals
  70. //
  71. //
  72. // Store the identifier string sent with every message.
  73. //
  74. char *ClLogIdentifier;
  75. //
  76. // Store the current logging options.
  77. //
  78. int ClLogOptions;
  79. //
  80. // Store the default facility code.
  81. //
  82. int ClLogFacility = LOG_USER;
  83. //
  84. // Store the mask of priorities to log. The default is to log everything.
  85. //
  86. ULONG ClLogMask = LOG_UPTO(LOG_DEBUG);
  87. //
  88. // Store the file descriptor for the logging socket.
  89. //
  90. int ClLogSocket = -1;
  91. //
  92. // Store the socket type.
  93. //
  94. int ClLogSocketType;
  95. //
  96. // ------------------------------------------------------------------ Functions
  97. //
  98. LIBC_API
  99. void
  100. openlog (
  101. const char *Identifier,
  102. int Options,
  103. int Facility
  104. )
  105. /*++
  106. Routine Description:
  107. This routine sets process attributes that affect subsequent calls to the
  108. syslog function.
  109. Arguments:
  110. Identifier - Supplies an identifier that is prepended to every message.
  111. Options - Supplies a mask of logging options. See LOG_* definitions.
  112. Facility - Supplies the default facility to be assigned to all messages
  113. that don't already have a facility. The initial default facility is
  114. LOG_USER.
  115. Return Value:
  116. None.
  117. --*/
  118. {
  119. char *NewIdentifier;
  120. //
  121. // TODO: Lock openlog when threading is supported.
  122. //
  123. if (Identifier != NULL) {
  124. NewIdentifier = strdup(Identifier);
  125. if (NewIdentifier != NULL) {
  126. if (ClLogIdentifier != NULL) {
  127. free(ClLogIdentifier);
  128. }
  129. ClLogIdentifier = NewIdentifier;
  130. }
  131. }
  132. ClpOpenLog(Options, Facility, FALSE);
  133. return;
  134. }
  135. LIBC_API
  136. int
  137. setlogmask (
  138. int PriorityMask
  139. )
  140. /*++
  141. Routine Description:
  142. This routine sets the log priority mask for the current process, and
  143. returns the previous mask. Calls to syslog with a priority not set in the
  144. given mask will be silently rejected. The default mask allows all
  145. priorities to be logged. A call to openlog is not requred prior to calling
  146. this function.
  147. Arguments:
  148. PriorityMask - Supplies the mask of priority bits to log. Use LOG_MASK and
  149. LOG_UPTO macros to create this value. If this value is zero, the
  150. current mask is returned but is not changed.
  151. Return Value:
  152. Returns the original mask before the potential change.
  153. --*/
  154. {
  155. int OriginalMask;
  156. if (PriorityMask != 0) {
  157. OriginalMask = RtlAtomicExchange32(&ClLogMask, PriorityMask);
  158. } else {
  159. OriginalMask = RtlAtomicOr32(&ClLogMask, 0);
  160. }
  161. return OriginalMask;
  162. }
  163. LIBC_API
  164. void
  165. vsyslog (
  166. int Priority,
  167. const char *Format,
  168. va_list ArgumentList
  169. )
  170. /*++
  171. Routine Description:
  172. This routine sends a message to an implementation-defined logging facility,
  173. which may log it to an implementation-defined system log, write it to the
  174. console, forward it over the network, or simply ignore it. The message
  175. header contains at least a timestamp and tag string.
  176. Arguments:
  177. Priority - Supplies the priority and facility of the message.
  178. Format - Supplies the printf-style format string to print.
  179. ArgumentList - Supplies the remaining arguments, dictated by the format
  180. string.
  181. Return Value:
  182. None.
  183. --*/
  184. {
  185. size_t BodyLength;
  186. int Console;
  187. time_t CurrentTime;
  188. char CurrentTimeBuffer[SYSLOG_TIME_BUFFER_SIZE];
  189. struct tm CurrentTimeFields;
  190. size_t HeaderLength;
  191. char Message[SYSLOG_MESSAGE_MAX];
  192. struct sigaction OldPipeAction;
  193. struct sigaction *OldPipeActionPointer;
  194. struct sigaction PipeAction;
  195. pid_t ProcessId;
  196. int SavedError;
  197. ssize_t SizeSent;
  198. //
  199. // Return quickly if the mask denies the log.
  200. //
  201. if (((LOG_MASK(LOG_PRI(Priority)) != 0) & ClLogMask) == 0) {
  202. return;
  203. }
  204. //
  205. // Handle invalid priority or facility bits.
  206. //
  207. if ((Priority & (~(LOG_PRIMASK | LOG_FACMASK))) != 0) {
  208. syslog(LOG_ERR | LOG_CONS | LOG_PERROR | LOG_PID,
  209. "syslog: Unknown facility/priority %x",
  210. Priority);
  211. Priority &= LOG_PRIMASK | LOG_FACMASK;
  212. }
  213. //
  214. // TODO: Lock vsyslog when threading is supported.
  215. //
  216. //
  217. // Set the facility if none was provided.
  218. //
  219. if ((Priority & LOG_FACMASK) == 0) {
  220. Priority |= ClLogFacility;
  221. }
  222. SavedError = errno;
  223. time(&CurrentTime);
  224. CurrentTimeBuffer[0] = '\0';
  225. strftime(CurrentTimeBuffer,
  226. sizeof(CurrentTimeBuffer),
  227. SYSLOG_TIME_FORMAT,
  228. localtime_r(&CurrentTime, &CurrentTimeFields));
  229. if (((ClLogOptions & LOG_PID) != 0) ||
  230. (ClLogIdentifier == NULL) ||
  231. (ClLogIdentifier[0] == '\0')) {
  232. ProcessId = getpid();
  233. HeaderLength = snprintf(Message,
  234. SYSLOG_MESSAGE_HEADER_MAX,
  235. "<%d>%s %s[%ld]: ",
  236. Priority,
  237. CurrentTimeBuffer,
  238. ClLogIdentifier,
  239. (long)ProcessId);
  240. } else {
  241. HeaderLength = snprintf(Message,
  242. SYSLOG_MESSAGE_HEADER_MAX,
  243. "<%d>%s %s: ",
  244. Priority,
  245. CurrentTimeBuffer,
  246. ClLogIdentifier);
  247. }
  248. errno = SavedError;
  249. BodyLength = vsnprintf(Message + HeaderLength,
  250. SYSLOG_MESSAGE_MAX - HeaderLength,
  251. Format,
  252. ArgumentList);
  253. //
  254. // Log to standard error if requested.
  255. //
  256. if ((ClLogOptions & LOG_PERROR) != 0) {
  257. ClpSyslogWrite(STDERR_FILENO, Message + HeaderLength, BodyLength);
  258. if (Message[HeaderLength + BodyLength] != '\n') {
  259. ClpSyslogWrite(STDERR_FILENO, "\n", 1);
  260. }
  261. }
  262. //
  263. // Prepare for a broken connection by ignoring SIGPIPE.
  264. //
  265. memset(&PipeAction, 0, sizeof(PipeAction));
  266. PipeAction.sa_handler = SIG_IGN;
  267. sigemptyset(&(PipeAction.sa_mask));
  268. OldPipeActionPointer = NULL;
  269. if (sigaction(SIGPIPE, &PipeAction, &OldPipeAction) == 0) {
  270. OldPipeActionPointer = &OldPipeAction;
  271. }
  272. if (ClLogSocket < 0) {
  273. ClpOpenLog(ClLogOptions, 0, TRUE);
  274. }
  275. //
  276. // For stream sockets, also send a null terminator to mark the end of the
  277. // record.
  278. //
  279. assert(HeaderLength + BodyLength < SYSLOG_MESSAGE_MAX);
  280. if (ClLogSocketType == SOCK_STREAM) {
  281. if ((HeaderLength + BodyLength + 1) < SYSLOG_MESSAGE_MAX) {
  282. BodyLength += 1;
  283. }
  284. Message[HeaderLength + BodyLength] = '\0';
  285. }
  286. SizeSent = 0;
  287. if (ClLogSocket >= 0) {
  288. SizeSent = send(ClLogSocket, Message, HeaderLength + BodyLength, 0);
  289. }
  290. if (SizeSent != HeaderLength + BodyLength) {
  291. if ((ClLogSocketType == SOCK_STREAM) && (BodyLength != 0)) {
  292. BodyLength -= 1;
  293. }
  294. ClpCloseLog();
  295. //
  296. // Log to the console if the send failed.
  297. //
  298. if ((ClLogOptions & LOG_CONS) != 0) {
  299. Console = open(SYSLOG_CONSOLE_PATH, O_WRONLY | O_NOCTTY);
  300. if (Console >= 0) {
  301. ClpSyslogWrite(Console, Message, HeaderLength + BodyLength);
  302. ClpSyslogWrite(Console, "\r\n", 2);
  303. close(Console);
  304. }
  305. }
  306. }
  307. //
  308. // Restore the pipe handler.
  309. //
  310. if (OldPipeActionPointer != NULL) {
  311. sigaction(SIGPIPE, &OldPipeAction, NULL);
  312. }
  313. return;
  314. }
  315. LIBC_API
  316. void
  317. syslog (
  318. int Priority,
  319. const char *Format,
  320. ...
  321. )
  322. /*++
  323. Routine Description:
  324. This routine sends a message to an implementation-defined logging facility,
  325. which may log it to an implementation-defined system log, write it to the
  326. console, forward it over the network, or simply ignore it. The message
  327. header contains at least a timestamp and tag string.
  328. Arguments:
  329. Priority - Supplies the priority and facility of the message.
  330. Format - Supplies the printf-style format string to print.
  331. ... - Supplies the remaining arguments, dictated by the format string.
  332. Return Value:
  333. None.
  334. --*/
  335. {
  336. va_list ArgumentList;
  337. va_start(ArgumentList, Format);
  338. vsyslog(Priority, Format, ArgumentList);
  339. va_end(ArgumentList);
  340. return;
  341. }
  342. LIBC_API
  343. void
  344. closelog (
  345. void
  346. )
  347. /*++
  348. Routine Description:
  349. This routine shuts down system logging facilities. They may be reopened by
  350. a subsequent call to openlog or syslog.
  351. Arguments:
  352. None.
  353. Return Value:
  354. None.
  355. --*/
  356. {
  357. if (ClLogIdentifier != NULL) {
  358. free(ClLogIdentifier);
  359. ClLogIdentifier = NULL;
  360. }
  361. //
  362. // TODO: Lock closelog once threading is implemented.
  363. //
  364. ClpCloseLog();
  365. return;
  366. }
  367. //
  368. // --------------------------------------------------------- Internal Functions
  369. //
  370. VOID
  371. ClpOpenLog (
  372. int Options,
  373. int Facility,
  374. BOOL MustConnect
  375. )
  376. /*++
  377. Routine Description:
  378. This routine opens the log file, potentially.
  379. Arguments:
  380. Options - Supplies a mask of logging options. See LOG_* definitions.
  381. Facility - Supplies the default facility to be assigned to all messages
  382. that don't already have a facility. The initial default facility is
  383. LOG_USER.
  384. MustConnect - Supplies a boolean indicating if this routine must attempt
  385. to connect (TRUE) or can delay according to the flags (FALSE).
  386. Return Value:
  387. None.
  388. --*/
  389. {
  390. struct sockaddr_un Address;
  391. int OriginalError;
  392. int Result;
  393. int Socket;
  394. UINTN Try;
  395. int Type;
  396. ClLogOptions = Options;
  397. if ((Facility != 0) && ((Facility & ~LOG_FACMASK) == 0)) {
  398. ClLogFacility = Facility;
  399. }
  400. //
  401. // Don't bother connecting right now if not necessary.
  402. //
  403. if ((MustConnect == FALSE) && ((ClLogOptions & LOG_NDELAY) == 0)) {
  404. return;
  405. }
  406. if (ClLogSocket >= 0) {
  407. return;
  408. }
  409. //
  410. // Loop trying different socket types.
  411. //
  412. for (Try = 0; Try < 2; Try += 1) {
  413. if (Try == 0) {
  414. Type = SOCK_DGRAM;
  415. } else {
  416. Type = SOCK_STREAM;
  417. }
  418. Socket = socket(AF_UNIX, Type, 0);
  419. if (Socket <= 0) {
  420. return;
  421. }
  422. fcntl(Socket, F_SETFD, FD_CLOEXEC);
  423. OriginalError = errno;
  424. memset(&Address, 0, sizeof(Address));
  425. Address.sun_family = AF_UNIX;
  426. strncpy(Address.sun_path, SYSLOG_PATH, UNIX_PATH_MAX);
  427. Result = connect(Socket, (struct sockaddr *)&Address, sizeof(Address));
  428. //
  429. // On failure, restore the original error and either try another type
  430. // or fail.
  431. //
  432. if (Result != 0) {
  433. errno = OriginalError;
  434. close(Socket);
  435. //
  436. // Set the new connected descriptor.
  437. //
  438. } else {
  439. assert(ClLogSocket == -1);
  440. ClLogSocket = Socket;
  441. ClLogSocketType = Type;
  442. }
  443. }
  444. return;
  445. }
  446. VOID
  447. ClpCloseLog (
  448. VOID
  449. )
  450. /*++
  451. Routine Description:
  452. This routine closes the syslog socket.
  453. Arguments:
  454. None.
  455. Return Value:
  456. None.
  457. --*/
  458. {
  459. if (ClLogSocket >= 0) {
  460. close(ClLogSocket);
  461. ClLogSocket = -1;
  462. }
  463. return;
  464. }
  465. VOID
  466. ClpSyslogWrite (
  467. int FileDescriptor,
  468. const void *Buffer,
  469. size_t ByteCount
  470. )
  471. /*++
  472. Routine Description:
  473. This routine attempts to write the specifed number of bytes to the given
  474. open file descriptor.
  475. Arguments:
  476. FileDescriptor - Supplies the file descriptor returned by the open function.
  477. Buffer - Supplies a pointer to the buffer containing the bytes to be
  478. written.
  479. ByteCount - Supplies the number of bytes to write.
  480. Return Value:
  481. Returns the number of bytes successfully written to the file.
  482. -1 on failure, and errno will contain more information.
  483. --*/
  484. {
  485. ssize_t BytesComplete;
  486. size_t TotalComplete;
  487. TotalComplete = 0;
  488. while (TotalComplete != ByteCount) {
  489. BytesComplete = write(FileDescriptor,
  490. Buffer + TotalComplete,
  491. ByteCount - TotalComplete);
  492. if (BytesComplete < 0) {
  493. if (errno == EINTR) {
  494. continue;
  495. }
  496. break;
  497. }
  498. TotalComplete += BytesComplete;
  499. }
  500. return;
  501. }