Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:33

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (C) 2014, 2022 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #ifdef HAVE_CONFIG_H
0029 #include "config.h"
0030 #endif
0031 
0032 #include <stdio.h>
0033 #include <errno.h>
0034 
0035 #include <sys/reent.h>
0036 #include <sys/stat.h>
0037 
0038 #include <rtems.h>
0039 #include <rtems/console.h>
0040 #include <rtems/imfs.h>
0041 #include <rtems/libcsupport.h>
0042 #include <rtems/sysinit.h>
0043 
0044 #include "tmacros.h"
0045 
0046 #ifndef _REENT_CLEANUP
0047 #define _REENT_CLEANUP(ptr) ((ptr)->__cleanup)
0048 #endif
0049 
0050 const char rtems_test_name[] = "NEWLIB 1";
0051 
0052 static const char stdio_file_path[] = "/stdio-file";
0053 
0054 static const char non_stdio_file_path[] = "/non-stdio-file";
0055 
0056 typedef enum {
0057   INIT,
0058   OPEN,
0059   WRITTEN,
0060   CLOSED
0061 } test_state;
0062 
0063 typedef struct {
0064   rtems_id main_task_id;
0065   rtems_id worker_task_id;
0066   test_state current;
0067   FILE *non_stdio_file;
0068   int non_stdio_fd;
0069 } test_context;
0070 
0071 static test_context test_instance;
0072 
0073 static void wake_up_main(const test_context *ctx)
0074 {
0075   rtems_status_code sc;
0076 
0077   sc = rtems_event_transient_send(ctx->main_task_id);
0078   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0079 }
0080 
0081 static void wait(void)
0082 {
0083   rtems_status_code sc;
0084 
0085   sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
0086   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0087 }
0088 
0089 /*
0090  * Check that rand() is properly initialized and returns the expected sequence
0091  * for default seed values. A call to rand() without any previous call to
0092  * srand() generates the same sequence as when srand() is first called with a
0093  * seed value of 1.
0094  */
0095 static void test_rand(void)
0096 {
0097   int rv;
0098 
0099   rv = rand();
0100   rtems_test_assert(rv == 1481765933);
0101   rv = rand();
0102   rtems_test_assert(rv == 1085377743);
0103   rv = rand();
0104   rtems_test_assert(rv == 1270216262);
0105 
0106   srand(1);
0107   rv = rand();
0108   rtems_test_assert(rv == 1481765933);
0109   rv = rand();
0110   rtems_test_assert(rv == 1085377743);
0111   rv = rand();
0112   rtems_test_assert(rv == 1270216262);
0113 }
0114 
0115 /*
0116  * Check that lrand48() is properly initialized and returns the expected
0117  * sequence for default seed values. A call to lrand48() without any previous
0118  * call to srand48() uses default constant initializer values set in the _seed
0119  * member of struct _rand48.
0120  */
0121 static void test_lrand48(void)
0122 {
0123   long rv;
0124 
0125   rv = lrand48();
0126   rtems_test_assert(rv == 851401618);
0127   rv = lrand48();
0128   rtems_test_assert(rv == 1804928587);
0129   rv = lrand48();
0130   rtems_test_assert(rv == 758783491);
0131 }
0132 
0133 static void stdio_file_worker(rtems_task_argument arg)
0134 {
0135   test_context *ctx = &test_instance;
0136   FILE *output;
0137   char buf[1] = { 'x' };
0138   size_t n;
0139 
0140   test_rand();
0141   test_lrand48();
0142 
0143   rtems_test_assert(_REENT_CLEANUP(_REENT) == NULL);
0144 
0145   output = stdout = fopen(&stdio_file_path[0], "r+");
0146   rtems_test_assert(stdout != NULL);
0147 
0148   /*
0149    * Check newlib's __sinit does not touch our assigned file pointer.
0150    */
0151   rtems_test_assert(_REENT_CLEANUP(_REENT) == NULL);
0152   rtems_test_assert(fflush(stdout) == 0);
0153   rtems_test_assert(_REENT_CLEANUP(_REENT) != NULL);
0154   rtems_test_assert(stdout == output);
0155 
0156   n = fwrite(&buf[0], sizeof(buf), 1, stdout);
0157   rtems_test_assert(n == 1);
0158 
0159   rtems_test_assert(ctx->current == OPEN);
0160 
0161   wake_up_main(ctx);
0162 
0163   rtems_test_assert(0);
0164 }
0165 
0166 static void non_stdio_file_worker(rtems_task_argument arg)
0167 {
0168   test_context *ctx = &test_instance;
0169   FILE *fp;
0170   char buf[1] = { 'y' };
0171   size_t n;
0172   int fd;
0173 
0174   fp = ctx->non_stdio_file = fopen(&non_stdio_file_path[0], "w");
0175   rtems_test_assert(fp != NULL);
0176 
0177   /* Get file descriptor of new global file stream, store it in text context */
0178   fd = fileno(fp);
0179   rtems_test_assert(fd != -1);
0180   ctx->non_stdio_fd = fd;
0181 
0182   n = fwrite(&buf[0], sizeof(buf), 1, fp);
0183   rtems_test_assert(n == 1);
0184 
0185   wake_up_main(ctx);
0186 
0187   rtems_test_assert(0);
0188 }
0189 
0190 static int handler_open(
0191   rtems_libio_t *iop,
0192   const char *path,
0193   int oflag,
0194   mode_t mode
0195 )
0196 {
0197   test_context *ctx = &test_instance;
0198 
0199   rtems_test_assert(ctx->current == INIT);
0200   ctx->current = OPEN;
0201 
0202   return 0;
0203 }
0204 
0205 static int handler_close(
0206   rtems_libio_t *iop
0207 )
0208 {
0209   test_context *ctx = &test_instance;
0210 
0211   rtems_test_assert(ctx->current == WRITTEN);
0212   ctx->current = CLOSED;
0213 
0214   return 0;
0215 }
0216 
0217 static ssize_t handler_read(
0218   rtems_libio_t *iop,
0219   void *buffer,
0220   size_t count
0221 )
0222 {
0223   rtems_test_assert(0);
0224 
0225   return 0;
0226 }
0227 
0228 static ssize_t handler_write(
0229   rtems_libio_t *iop,
0230   const void *buffer,
0231   size_t count
0232 )
0233 {
0234   test_context *ctx = &test_instance;
0235 
0236   rtems_test_assert(ctx->current == OPEN);
0237   ctx->current = WRITTEN;
0238 
0239   iop->offset += count;
0240 
0241   return (ssize_t) count;
0242 }
0243 
0244 static int handler_ioctl(
0245   rtems_libio_t *iop,
0246   ioctl_command_t request,
0247   void *buffer
0248 )
0249 {
0250   rtems_test_assert(0);
0251 
0252   return 0;
0253 }
0254 
0255 static off_t handler_lseek(
0256   rtems_libio_t *iop,
0257   off_t length,
0258   int whence
0259 )
0260 {
0261   rtems_test_assert(0);
0262 
0263   return 0;
0264 }
0265 
0266 static int handler_ftruncate(
0267   rtems_libio_t *iop,
0268   off_t length
0269 )
0270 {
0271   rtems_test_assert(0);
0272 
0273   return 0;
0274 }
0275 
0276 static int handler_fsync(
0277   rtems_libio_t *iop
0278 )
0279 {
0280   rtems_test_assert(0);
0281 
0282   return 0;
0283 }
0284 
0285 static int handler_fdatasync(
0286   rtems_libio_t *iop
0287 )
0288 {
0289   rtems_test_assert(0);
0290 
0291   return 0;
0292 }
0293 
0294 static int handler_fcntl(
0295   rtems_libio_t *iop,
0296   int cmd
0297 )
0298 {
0299   rtems_test_assert(0);
0300 
0301   return 0;
0302 }
0303 
0304 static ssize_t handler_readv(
0305   rtems_libio_t *iop,
0306   const struct iovec *iov,
0307   int iovcnt,
0308   ssize_t total
0309 )
0310 {
0311   rtems_test_assert(0);
0312 
0313   return 0;
0314 }
0315 
0316 static ssize_t handler_writev(
0317   rtems_libio_t *iop,
0318   const struct iovec *iov,
0319   int iovcnt,
0320   ssize_t total
0321 )
0322 {
0323   rtems_test_assert(0);
0324 
0325   return 0;
0326 }
0327 
0328 static const rtems_filesystem_file_handlers_r node_handlers = {
0329   .open_h = handler_open,
0330   .close_h = handler_close,
0331   .read_h = handler_read,
0332   .write_h = handler_write,
0333   .ioctl_h = handler_ioctl,
0334   .lseek_h = handler_lseek,
0335   .fstat_h = rtems_filesystem_default_fstat,
0336   .ftruncate_h = handler_ftruncate,
0337   .fsync_h = handler_fsync,
0338   .fdatasync_h = handler_fdatasync,
0339   .fcntl_h = handler_fcntl,
0340   .readv_h = handler_readv,
0341   .writev_h = handler_writev
0342 };
0343 
0344 static const IMFS_node_control node_control = IMFS_GENERIC_INITIALIZER(
0345   &node_handlers,
0346   IMFS_node_initialize_default,
0347   IMFS_node_destroy_default
0348 );
0349 
0350 static void create_and_run_worker(test_context *ctx, rtems_task_entry entry)
0351 {
0352   rtems_status_code sc;
0353 
0354   sc = rtems_task_create(
0355     rtems_build_name('W', 'O', 'R', 'K'),
0356     2,
0357     RTEMS_MINIMUM_STACK_SIZE,
0358     RTEMS_DEFAULT_MODES,
0359     RTEMS_DEFAULT_ATTRIBUTES,
0360     &ctx->worker_task_id
0361   );
0362   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0363 
0364   sc = rtems_task_start(ctx->worker_task_id, entry, 0);
0365   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0366 
0367   wait();
0368 
0369   sc = rtems_task_delete(ctx->worker_task_id);
0370   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0371 }
0372 
0373 /*
0374  * Check that a FILE opened by a task and assigned to a stdio stream is closed
0375  * during thread termination. Ensure that resources are returned to the system.
0376  */
0377 static void test_stdio_file(test_context *ctx)
0378 {
0379   int rv;
0380   rtems_resource_snapshot snapshot;
0381 
0382   rtems_resource_snapshot_take(&snapshot);
0383 
0384   rv = IMFS_make_generic_node(
0385     &stdio_file_path[0],
0386     S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
0387     &node_control,
0388     NULL
0389   );
0390   rtems_test_assert(rv == 0);
0391 
0392   create_and_run_worker(ctx, stdio_file_worker);
0393 
0394   rv = unlink(&stdio_file_path[0]);
0395   rtems_test_assert(rv == 0);
0396 
0397   rtems_test_assert(rtems_resource_snapshot_check(&snapshot));
0398 }
0399 
0400 /*
0401  * Open a global FILE object from a task but do not assign it to a stdio
0402  * stream. The FILE is not closed upon thread termination.
0403  */
0404 static void test_non_stdio_file(test_context *ctx)
0405 {
0406   create_and_run_worker(ctx, non_stdio_file_worker);
0407 }
0408 
0409 /*
0410  * This exit handler will be called last among the functions registered with
0411  * atexit(). Check that stdio file descriptors are closed. The Newlib cleanup
0412  * handler has not yet run, so the stdio FILE objects themselves are still
0413  * open.
0414  */
0415 static void check_after_libio_exit(void)
0416 {
0417   test_context *ctx = &test_instance;
0418   struct stat unused;
0419   int rv;
0420 
0421   errno = 0;
0422   rv = fstat(0, &unused);
0423   rtems_test_assert(rv == -1);
0424   rtems_test_assert(errno == EBADF);
0425 
0426   errno = 0;
0427   rv = fstat(1, &unused);
0428   rtems_test_assert(rv == -1);
0429   rtems_test_assert(errno == EBADF);
0430 
0431   errno = 0;
0432   rv = fstat(2, &unused);
0433   rtems_test_assert(rv == -1);
0434   rtems_test_assert(errno == EBADF);
0435 
0436   rtems_test_assert(stdin->_flags != 0);
0437   rtems_test_assert(stdout->_flags != 0);
0438   rtems_test_assert(stderr->_flags != 0);
0439 
0440   /*
0441    * The non-stdio file and its file descriptor should be still open at this
0442    * point.
0443    */
0444   rv = fstat(ctx->non_stdio_fd, &unused);
0445   rtems_test_assert(rv == 0);
0446   rtems_test_assert(ctx->non_stdio_file->_flags != 0);
0447 }
0448 
0449 static void register_exit_handler_before_libio_exit(void)
0450 {
0451   int rv;
0452 
0453   rv = atexit(check_after_libio_exit);
0454   rtems_test_assert(rv == 0);
0455 }
0456 
0457 /*
0458  * Register the exit handler before rtems_libio_exit() so that
0459  * check_after_libio_exit() is called after rtems_libio_exit().  The exit()
0460  * handlers are in a LIFO list.
0461  */
0462 RTEMS_SYSINIT_ITEM(register_exit_handler_before_libio_exit,
0463     RTEMS_SYSINIT_STD_FILE_DESCRIPTORS, RTEMS_SYSINIT_ORDER_FIRST);
0464 
0465 /*
0466  * At this point, neither the functions registered with atexit() nor the Newlib
0467  * cleanup procedures have been called. Therefore, stdio file descriptors
0468  * should be open and stdio FILE object flags should be non-zero.
0469  */
0470 static void test_exit_handling(test_context *ctx)
0471 {
0472   struct stat unused;
0473   int rv;
0474 
0475   rv = fstat(0, &unused);
0476   rtems_test_assert(rv == 0);
0477 
0478   rv = fstat(1, &unused);
0479   rtems_test_assert(rv == 0);
0480 
0481   rv = fstat(2, &unused);
0482   rtems_test_assert(rv == 0);
0483 
0484   rtems_test_assert(stdin->_flags != 0);
0485   rtems_test_assert(stdout->_flags != 0);
0486   rtems_test_assert(stderr->_flags != 0);
0487 
0488   /*
0489    * The file descriptor of the non-stdio file should still be open; the FILE
0490    * object flags should still be non-zero.
0491    */
0492   rv = fstat(ctx->non_stdio_fd, &unused);
0493   rtems_test_assert(rv == 0);
0494   rtems_test_assert(ctx->non_stdio_file->_flags != 0);
0495 
0496   /* Run exit handlers and Newlib cleanup procedures */
0497   exit(0);
0498 }
0499 
0500 static void Init(rtems_task_argument arg)
0501 {
0502   test_context *ctx = &test_instance;
0503   FILE *file;
0504   int rv;
0505 
0506   TEST_BEGIN();
0507   test_rand();
0508   test_lrand48();
0509 
0510   ctx->main_task_id = rtems_task_self();
0511 
0512   /* Fill dynamic file pool in Newlib */
0513   file = fopen(CONSOLE_DEVICE_NAME, "r+");
0514   rtems_test_assert(file != NULL);
0515   rv = fclose(file);
0516   rtems_test_assert(rv == 0);
0517 
0518   test_stdio_file(ctx);
0519   test_non_stdio_file(ctx);
0520   test_exit_handling(ctx);
0521 }
0522 
0523 static void fatal_extension(
0524   rtems_fatal_source source,
0525   bool always_set_to_false,
0526   rtems_fatal_code error
0527 )
0528 {
0529   if (
0530     source == RTEMS_FATAL_SOURCE_EXIT
0531       && !always_set_to_false
0532       && error == 0
0533   ) {
0534     /*
0535      * Final conditions check after exit handlers and Newlib cleanup procedures
0536      * have run. File descriptors and FILE objects themselves are closed.
0537      */
0538     struct stat unused;
0539     int rv;
0540     test_context *ctx = &test_instance;
0541 
0542     errno = 0;
0543     rv = fstat(0, &unused);
0544     rtems_test_assert(rv == -1);
0545     rtems_test_assert(errno == EBADF);
0546 
0547     errno = 0;
0548     rv = fstat(1, &unused);
0549     rtems_test_assert(rv == -1);
0550     rtems_test_assert(errno == EBADF);
0551 
0552     errno = 0;
0553     rv = fstat(2, &unused);
0554     rtems_test_assert(rv == -1);
0555     rtems_test_assert(errno == EBADF);
0556 
0557     rtems_test_assert(stdin->_flags == 0);
0558     rtems_test_assert(stdout->_flags == 0);
0559     rtems_test_assert(stderr->_flags == 0);
0560 
0561     /* The non-stdio file and its file descriptor should be closed */
0562     errno = 0;
0563     rv = fstat(ctx->non_stdio_fd, &unused);
0564     rtems_test_assert(rv == -1);
0565     rtems_test_assert(errno == EBADF);
0566     rtems_test_assert(ctx->non_stdio_file->_flags == 0);
0567 
0568     TEST_END();
0569   }
0570 }
0571 
0572 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
0573 #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
0574 
0575 #define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
0576 
0577 #define CONFIGURE_MAXIMUM_TASKS 2
0578 
0579 #define CONFIGURE_INITIAL_EXTENSIONS \
0580   { .fatal = fatal_extension }, \
0581   RTEMS_TEST_INITIAL_EXTENSION
0582 
0583 #define CONFIGURE_RTEMS_INIT_TASKS_TABLE
0584 
0585 #define CONFIGURE_INIT
0586 
0587 #include <rtems/confdefs.h>