1 /**************************************************************************** 2 * fs/driver/fs_devsyslog.c 3 * 4 * Copyright (C) 2012 Gregory Nutt. All rights reserved. 5 * Author: Gregory Nutt <gnutt@nuttx.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name NuttX nor the names of its contributors may be 18 * used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 * 34 ****************************************************************************/ 35 36 /**************************************************************************** 37 * Included Files 38 ****************************************************************************/ 39 40 #include "los_task.h" 41 #include "vfs_config.h" 42 #include "sys/types.h" 43 #include "stdint.h" 44 #include "stdio.h" 45 #include "unistd.h" 46 #include "fcntl.h" 47 #include "semaphore.h" 48 #include "assert.h" 49 #include "fs/driver.h" 50 #include "inode/inode.h" 51 52 #if defined(CONFIG_SYSLOG) && defined(CONFIG_SYSLOG_CHAR) 53 54 /**************************************************************************** 55 * Pre-processor Definitions 56 ****************************************************************************/ 57 58 /* Open the device/file write-only, try to create (file) it if it doesn't 59 * exist, if the file that already exists, then append the new log data to 60 * end of the file. 61 */ 62 63 #define SYSLOG_OFLAGS (O_WRONLY | O_CREAT | O_APPEND) 64 65 /* An invalid thread ID */ 66 67 #define NO_HOLDER ((pid_t)-1) 68 69 /**************************************************************************** 70 * Private Types 71 ****************************************************************************/ 72 73 /* This enumeration represents the state of the SYSLOG device interface */ 74 75 enum syslog_state_e 76 { 77 SYSLOG_UNINITIALIZED = 0, /* SYSLOG has not been initialized */ 78 SYSLOG_INITIALIZING, /* SYSLOG is being initialized */ 79 SYSLOG_REOPEN, /* SYSLOG open failed... try again later */ 80 SYSLOG_FAILURE, /* SYSLOG open failed... don't try again */ 81 SYSLOG_OPENED, /* SYSLOG device is open and ready to use */ 82 }; 83 84 /* This structure contains all SYSLOGing state information */ 85 86 struct syslog_dev_s 87 { 88 uint8_t sl_state; /* See enum syslog_state_e */ 89 sem_t sl_sem; /* Enforces mutually exclusive access */ 90 pid_t sl_holder; /* PID of the thread that holds the semaphore */ 91 struct file sl_file; /* The syslog file structure */ 92 }; 93 94 /**************************************************************************** 95 * Private Function Prototypes 96 ****************************************************************************/ 97 98 /**************************************************************************** 99 * Private Data 100 ****************************************************************************/ 101 102 /* This is the device structure for the console or syslogging function. */ 103 104 static struct syslog_dev_s g_sysdev; 105 static const uint8_t g_syscrlf[2] = 106 { 107 '\r', '\n' 108 }; 109 110 /**************************************************************************** 111 * Private Functions 112 ****************************************************************************/ 113 114 /**************************************************************************** 115 * Name: syslog_takesem 116 * 117 * Description: 118 * Write to the syslog device 119 * 120 ****************************************************************************/ 121 syslog_takesem(void)122 static inline int syslog_takesem(void) 123 { 124 pid_t me = getpid(); 125 int ret; 126 127 /* Does this thread already hold the semaphore? That could happen if 128 * we wer called recursively, i.e., if the logic kicked off by 129 * syslog_write() where to generate more debug output. Return an error 130 * in that case. 131 */ 132 133 if (g_sysdev.sl_holder == me) 134 { 135 /* Return an error (instead of deadlocking) */ 136 137 return -EWOULDBLOCK; 138 } 139 140 /* Either the semaphore is available or is currently held by another 141 * thread. Wait for it to become available. 142 */ 143 144 ret = sem_wait(&g_sysdev.sl_sem); 145 if (ret < 0) 146 { 147 return -get_errno(); 148 } 149 150 /* We hold the semaphore. We can safely mark ourself as the holder 151 * of the semaphore. 152 */ 153 154 g_sysdev.sl_holder = me; 155 return OK; 156 } 157 158 /**************************************************************************** 159 * Name: syslog_givesem 160 * 161 * Description: 162 * Write to the syslog device 163 * 164 ****************************************************************************/ 165 syslog_givesem(void)166 static inline void syslog_givesem(void) 167 { 168 #ifdef CONFIG_DEBUG 169 pid_t me = getpid(); 170 DEBUGASSERT(g_sysdev.sl_holder == me); 171 #endif 172 173 /* Relinquish the semaphore */ 174 175 g_sysdev.sl_holder = NO_HOLDER; 176 (void)sem_post(&g_sysdev.sl_sem); 177 } 178 179 /**************************************************************************** 180 * Name: syslog_write 181 * 182 * Description: 183 * Write to the syslog device 184 * 185 ****************************************************************************/ 186 syslog_write(const void * buf,size_t nbytes)187 static inline ssize_t syslog_write(const void *buf, size_t nbytes) 188 { 189 struct inode *inode_ptr; 190 191 /* Let the driver perform the write */ 192 193 inode_ptr = g_sysdev.sl_file.f_inode; 194 return inode_ptr->u.i_ops->write(&g_sysdev.sl_file, (const char *)buf, nbytes); 195 } 196 197 /**************************************************************************** 198 * Name: syslog_flush 199 * 200 * Description: 201 * Flush any buffer data in the file system to media. 202 * 203 ****************************************************************************/ 204 205 #ifndef CONFIG_DISABLE_MOUNTPOINT syslog_flush(void)206 static inline void syslog_flush(void) 207 { 208 struct inode *inode_ptr = g_sysdev.sl_file.f_inode; 209 210 /* Is this a mountpoint? Does it support the sync method? */ 211 212 if (INODE_IS_MOUNTPT(inode_ptr) && inode_ptr->u.i_mops->sync) 213 { 214 /* Yes... synchronize to the stream */ 215 216 (void)inode_ptr->u.i_mops->sync(&g_sysdev.sl_file); 217 } 218 } 219 #endif 220 221 /**************************************************************************** 222 * Public Functions 223 ****************************************************************************/ 224 225 /**************************************************************************** 226 * Name: syslog_initialize 227 * 228 * Description: 229 * Initialize to use the character device (or file) at 230 * CONFIG_SYSLOG_DEVPATH as the SYSLOG sink. 231 * 232 * NOTE that this implementation excludes using a network connection as 233 * SYSLOG device. That would be a good extension. 234 * 235 ****************************************************************************/ 236 syslog_initialize(void)237 int syslog_initialize(void) 238 { 239 struct inode *inode_ptr; 240 const char *relpath = NULL; 241 int ret; 242 struct inode_search_s desc; 243 244 /* At this point, the only expected states are SYSLOG_UNINITIALIZED or 245 * SYSLOG_REOPEN.. Not SYSLOG_INITIALIZING, SYSLOG_FAILURE, SYSLOG_OPENED. 246 */ 247 248 DEBUGASSERT(g_sysdev.sl_state == SYSLOG_UNINITIALIZED || 249 g_sysdev.sl_state == SYSLOG_REOPEN); 250 251 g_sysdev.sl_state = SYSLOG_INITIALIZING; 252 253 /* Try to open the device. 254 * 255 * Note that we cannot just call open. The syslog device must work on all 256 * threads. Open returns a file descriptor that is valid only for the 257 * task that opened the device (and its pthread children). Instead, we 258 * essentially re-implement the guts of open() here so that we can get to 259 * the thread-independent structures of the inode. 260 */ 261 262 /* Get an inode for this file/device */ 263 264 SETUP_SEARCH(&desc, CONFIG_SYSLOG_DEVPATH, false); 265 ret = inode_find(&desc); 266 267 /* Get the search results */ 268 269 if (ret < 0) 270 { 271 /* The inode was not found. In this case, we will attempt to re-open 272 * the device repeatedly. The assumption is that the device path is 273 * valid but that the driver has not yet been registered. 274 */ 275 276 g_sysdev.sl_state = SYSLOG_REOPEN; 277 return -EACCES; 278 } 279 inode_ptr = desc.node; 280 relpath = desc.relpath; 281 282 /* Verify that the inode is valid and either a character driver or a 283 * mountpoint. 284 */ 285 286 #ifndef CONFIG_DISABLE_MOUNTPOINT 287 if ((!INODE_IS_DRIVER(inode_ptr) && !INODE_IS_MOUNTPT(inode_ptr))) 288 #else 289 if (!INODE_IS_DRIVER(inode_ptr)) 290 #endif 291 { 292 ret = -ENXIO; 293 goto errout_with_inode; 294 } 295 296 /* Make sure that the "entity" at this inode supports write access */ 297 298 if (!inode_ptr->u.i_ops || !inode_ptr->u.i_ops->write) 299 { 300 ret = -EACCES; 301 goto errout_with_inode; 302 } 303 304 /* Initialize the file structure */ 305 306 g_sysdev.sl_file.f_oflags = SYSLOG_OFLAGS; 307 g_sysdev.sl_file.f_pos = 0; 308 g_sysdev.sl_file.f_inode = inode_ptr; 309 310 /* Perform the low-level open operation. */ 311 312 ret = OK; 313 if (inode_ptr->u.i_ops->open) 314 { 315 /* Is the inode a mountpoint? */ 316 317 #ifndef CONFIG_DISABLE_MOUNTPOINT 318 if (INODE_IS_MOUNTPT(inode_ptr)) 319 { 320 /* Yes. Open the device write-only, try to create it if it 321 * doesn't exist, if the file that already exists, then append the 322 * new log data to end of the file. 323 */ 324 325 ret = inode_ptr->u.i_mops->open(&g_sysdev.sl_file, relpath, 326 SYSLOG_OFLAGS, 0666); 327 } 328 329 /* No... then it must be a character driver in the NuttX pseudo- 330 * file system. 331 */ 332 333 else 334 #endif 335 { 336 ret = inode_ptr->u.i_ops->open(&g_sysdev.sl_file); 337 } 338 } 339 340 /* Was the file/device successfully opened? */ 341 342 if (ret < 0) 343 { 344 ret = -ret; 345 goto errout_with_inode; 346 } 347 348 /* The SYSLOG device is open and ready for writing. */ 349 350 (void)sem_init(&g_sysdev.sl_sem, 0, 1); 351 g_sysdev.sl_holder = NO_HOLDER; 352 g_sysdev.sl_state = SYSLOG_OPENED; 353 return OK; 354 355 errout_with_inode: 356 inode_release(inode_ptr); 357 g_sysdev.sl_state = SYSLOG_FAILURE; 358 return ret; 359 } 360 361 /**************************************************************************** 362 * Name: syslog_putc 363 * 364 * Description: 365 * This is the low-level system logging interface. The debugging/syslogging 366 * interfaces are syslog() and lowsyslog(). The difference is is that 367 * the syslog() function writes to syslogging device (usually fd=1, stdout) 368 * whereas lowsyslog() uses a lower level interface that works from 369 * interrupt handlers. This function is a a low-level interface used to 370 * implement lowsyslog(). 371 * 372 ****************************************************************************/ 373 syslog_putc(int ch)374 int syslog_putc(int ch) 375 { 376 ssize_t nbytes; 377 uint8_t uch; 378 int errcode; 379 int ret; 380 381 /* Ignore any output: 382 * 383 * (1) Before the SYSLOG device has been initialized. This could happen 384 * from debug output that occurs early in the boot sequence before 385 * syslog_initialize() is called (SYSLOG_UNINITIALIZED). 386 * (2) While the device is being initialized. The case could happen if 387 * debug output is generated while syslog_initialize() executes 388 * (SYSLOG_INITIALIZING). 389 * (3) While we are generating SYSLOG output. The case could happen if 390 * debug output is generated while syslog_putc() executes 391 * (This case is actually handled inside of syslog_semtake()). 392 * (4) Any debug output generated from interrupt handlers. A disadvantage 393 * of using the generic character device for the SYSLOG is that it 394 * cannot handle debug output generated from interrupt level handlers. 395 * (5) Any debug output generated from the IDLE loop. The character 396 * driver interface is blocking and the IDLE thread is not permitted 397 * to block. 398 * (6) If an irrecoverable failure occurred during initialization. In 399 * this case, we won't ever bother to try again (ever). 400 * 401 * NOTE: That the third case is different. It applies only to the thread 402 * that currently holds the sl_sem sempaphore. Other threads should wait. 403 * that is why that case is handled in syslog_semtake(). 404 */ 405 406 /* Cases (4) and (5) */ 407 408 if (OS_INT_ACTIVE || getpid() == 0) 409 { 410 errcode = ENOSYS; 411 goto errout_with_errcode; 412 } 413 414 /* We can save checks in the usual case: That after the SYSLOG device 415 * has been successfully opened. 416 */ 417 418 if (g_sysdev.sl_state != SYSLOG_OPENED) 419 { 420 /* Case (1) and (2) */ 421 422 if (g_sysdev.sl_state == SYSLOG_UNINITIALIZED || 423 g_sysdev.sl_state == SYSLOG_INITIALIZING) 424 { 425 errcode = EAGAIN; /* Can't access the SYSLOG now... maybe next time? */ 426 goto errout_with_errcode; 427 } 428 429 /* Case (6) */ 430 431 if (g_sysdev.sl_state == SYSLOG_FAILURE) 432 { 433 errcode = ENXIO; /* There is no SYSLOG device */ 434 goto errout_with_errcode; 435 } 436 437 /* syslog_initialize() is called as soon as enough of the operating 438 * system is in place to support the open operation... but it is 439 * possible that the SYSLOG device is not yet registered at that time. 440 * In this case, we know that the system is sufficiently initialized 441 * to support an attempt to re-open the SYSLOG device. 442 * 443 * NOTE that the scheduler is locked. That is because we do not have 444 * fully initialized semaphore capability until the SYSLOG device is 445 * successfully initialized 446 */ 447 448 LOS_TaskLock(); 449 if (g_sysdev.sl_state == SYSLOG_REOPEN) 450 { 451 /* Try again to initialize the device. We may do this repeatedly 452 * because the log device might be something that was not ready 453 * the first time that syslog_initializee() was called (such as a 454 * USB serial device that has not yet been connected or a file in 455 * an NFS mounted file system that has not yet been mounted). 456 */ 457 458 ret = syslog_initialize(); 459 if (ret < 0) 460 { 461 LOS_TaskUnlock(); 462 errcode = -ret; 463 goto errout_with_errcode; 464 } 465 } 466 467 LOS_TaskUnlock(); 468 DEBUGASSERT(g_sysdev.sl_state == SYSLOG_OPENED); 469 } 470 471 /* Ignore carriage returns */ 472 473 if (ch == '\r') 474 { 475 return ch; 476 } 477 478 /* The syslog device is ready for writing and we have something of 479 * value to write. 480 */ 481 482 ret = syslog_takesem(); 483 if (ret < 0) 484 { 485 /* We probably already hold the semaphore and were probably 486 * re-entered by the logic kicked off by syslog_write(). 487 * We might also have been interrupted by a signal. Either 488 * way, we are outta here. 489 */ 490 491 errcode = -ret; 492 goto errout_with_errcode; 493 } 494 495 /* Pre-pend a newline with a carriage return. */ 496 497 if (ch == '\n') 498 { 499 /* Write the CR-LF sequence */ 500 501 nbytes = syslog_write(g_syscrlf, 2); 502 503 /* Synchronize the file when each CR-LF is encountered (i.e., 504 * implements line buffering always). 505 */ 506 507 #ifndef CONFIG_DISABLE_MOUNTPOINT 508 if (nbytes > 0) 509 { 510 syslog_flush(); 511 } 512 #endif 513 } 514 else 515 { 516 /* Write the non-newline character (and don't flush) */ 517 518 uch = (uint8_t)ch; 519 nbytes = syslog_write(&uch, 1); 520 } 521 522 syslog_givesem(); 523 524 /* Check if the write was successful. If not, nbytes will be 525 * a negated errno value. 526 */ 527 528 if (nbytes < 0) 529 { 530 errcode = -ret; 531 goto errout_with_errcode; 532 } 533 534 return ch; 535 536 errout_with_errcode: 537 if (errcode != 0) 538 { 539 set_errno(errcode); 540 } 541 return EOF; 542 } 543 544 #endif /* CONFIG_SYSLOG && CONFIG_SYSLOG_CHAR */ 545