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