µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
io.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
3 * Copyright (c) 2015-2025 Liviu Ionescu. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software
6 * for any purpose is hereby granted, under the terms of the MIT license.
7 *
8 * If a copy of the license was not distributed with this file, it can
9 * be obtained from https://opensource.org/licenses/mit.
10 */
11
12#if defined(OS_USE_OS_APP_CONFIG_H)
13#include <cmsis-plus/os-app-config.h>
14#endif
15
23
25
26#include <cassert>
27#include <cerrno>
28#include <cstdarg>
29
30// ----------------------------------------------------------------------------
31
32#if defined(__clang__)
33#pragma clang diagnostic ignored "-Wc++98-compat"
34#endif
35
36// ----------------------------------------------------------------------------
37
38// Variadic calls are processed in two steps, first prepare a
39// va_list structure, then call implementation functions like doOpen()
40// doIoctl(), that use 'va_list args'.
41
42namespace os
43{
44 namespace posix
45 {
46 // ------------------------------------------------------------------------
47
48 io*
49 open (const char* path, int oflag, ...)
50 {
51 // Forward to the variadic version of the function.
52 std::va_list args;
53 va_start (args, oflag);
54 io* const ret = vopen (path, oflag, args);
55 va_end (args);
56
57 return ret;
58 }
59
65 io*
66 vopen (const char* path, int oflag, std::va_list args)
67 {
68#if defined(OS_TRACE_POSIX_IO_IO)
69 trace::printf ("io::%s(\"%s\")\n", __func__, path ? path : "");
70#endif
71
72 if (path == nullptr)
73 {
74 errno = EFAULT;
75 return nullptr;
76 }
77
78 if (*path == '\0')
79 {
80 errno = ENOENT;
81 return nullptr;
82 }
83
84 errno = 0;
85
87 while (true)
88 {
89 // Check if path is a device.
91 if (io != nullptr)
92 {
93 // If so, use the implementation to open the device.
94 int oret = static_cast<device*> (io)->vopen (path, oflag, args);
95 if (oret < 0)
96 {
97 // Open failed.
98 return nullptr;
99 }
100
101 // File descriptor already allocated by device.
102 break;
103 }
104
105 // Check if a regular file.
106 auto adjusted_path = path;
107 auto* const fs
109
110 // The manager will return null if there are no file systems
111 // registered, no need to check this condition separately.
112 if (fs == nullptr)
113 {
114 errno = EBADF;
115 return nullptr;
116 }
117
118 // Use the file system implementation to open the file, using
119 // the adjusted path (mount point prefix removed).
120 io = fs->vopen (adjusted_path, oflag, args);
121 if (io == nullptr)
122 {
123 // Open failed.
124 return nullptr;
125 }
126
127 break;
128 }
129
130 // Return a valid pointer to an object derived from io, or nullptr.
131
132#if defined(OS_TRACE_POSIX_IO_IO)
133 trace::printf ("io::%s(\"%s\")=%p fd=%d\n", __func__, path, io,
134 io->file_descriptor ());
135#endif
136 return io;
137 }
138
139 // ========================================================================
140
141 io::io (io_impl& impl, type t)
142 : impl_ (impl), //
143 type_ (static_cast<type_t> (t))
144 {
145#if defined(OS_TRACE_POSIX_IO_IO)
146 trace::printf ("io::%s()=%p\n", __func__, this);
147#endif
148
149 file_descriptor_ = no_file_descriptor;
150 }
151
153 {
154#if defined(OS_TRACE_POSIX_IO_IO)
155 trace::printf ("io::%s() @%p\n", __func__, this);
156#endif
157
158 file_descriptor_ = no_file_descriptor;
159 }
160
161 // ------------------------------------------------------------------------
162
163 int
165 {
166#if defined(OS_TRACE_POSIX_IO_IO)
167 trace::printf ("io::%s() @%p\n", __func__, this);
168#endif
169
170 if (!impl ().do_is_opened ())
171 {
172 errno = EBADF; // Not opened.
173 return -1;
174 }
175
176 errno = 0;
177
178 // Execute the implementation specific code.
179 int ret = impl ().do_close ();
180
181 // Remove this IO from the file descriptors registry.
182 file_descriptors_manager::deallocate (file_descriptor_);
183 file_descriptor_ = no_file_descriptor;
184
185 return ret;
186 }
187
188 io*
190 {
191#if defined(OS_TRACE_POSIX_IO_IO)
192 trace::printf ("io::%s() @%p\n", __func__, this);
193#endif
194
196 if (fd < 0)
197 {
198 // If allocation failed, close this object.
199 impl ().do_close ();
201 return nullptr;
202 }
203
204#if defined(OS_TRACE_POSIX_IO_IO)
205 trace::printf ("io::%s() @%p fd=%d\n", __func__, this, fd);
206#endif
207
208 // Return a valid pointer to an object derived from `io`.
209 return this;
210 }
211
212 // ------------------------------------------------------------------------
213
214 // All these wrappers are required to clear 'errno'.
215
216 ssize_t
217 io::read (void* buf, std::size_t nbyte)
218 {
219#if defined(OS_TRACE_POSIX_IO_IO)
220 trace::printf ("io::%s(0x0%X, %u) @%p\n", __func__, buf, nbyte, this);
221#endif
222
223 if (buf == nullptr)
224 {
225 errno = EFAULT;
226 return -1;
227 }
228
229 if (!impl ().do_is_opened ())
230 {
231 errno = EBADF; // Not opened.
232 return -1;
233 }
234
235 if (!impl ().do_is_connected ())
236 {
237 errno = EIO; // Not opened.
238 return -1;
239 }
240
241 errno = 0;
242
243 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html
244 // Before any action described below is taken, and if nbyte is zero,
245 // the read() function may detect and return errors as described below.
246 // In the absence of errors, or if error detection is not performed,
247 // the read() function shall return zero and have no other results.
248 if (nbyte == 0)
249 {
250 return 0; // Nothing to do.
251 }
252
253 // Execute the implementation specific code.
254 ssize_t ret = impl ().do_read (buf, nbyte);
255 if (ret >= 0)
256 {
257 impl ().offset_ += ret;
258 }
259
260#if defined(OS_TRACE_POSIX_IO_IO)
261 trace::printf ("io::%s(0x0%X, %u) @%p n=%d\n", __func__, buf, nbyte,
262 this, ret);
263#endif
264 return ret;
265 }
266
267 ssize_t
268 io::write (const void* buf, std::size_t nbyte)
269 {
270#if defined(OS_TRACE_POSIX_IO_IO)
271 trace::printf ("io::%s(0x0%X, %u) @%p\n", __func__, buf, nbyte, this);
272#endif
273
274 if (buf == nullptr)
275 {
276 errno = EFAULT;
277 return -1;
278 }
279
280 if (!impl ().do_is_opened ())
281 {
282 errno = EBADF; // Not opened.
283 return -1;
284 }
285
286 if (!impl ().do_is_connected ())
287 {
288 errno = EIO; // Not opened.
289 return -1;
290 }
291
292 errno = 0;
293
294 // http://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html
295 // Before any action described below is taken, and if nbyte is zero
296 // and the file is a regular file, the write() function may detect and
297 // return errors as described below. In the absence of errors, or if
298 // error detection is not performed, the write() function shall return
299 // zero and have no other results. If nbyte is zero and the file is
300 // not a regular file, the results are unspecified.
301 if (nbyte == 0)
302 {
303 return 0; // Nothing to do.
304 }
305
306 // Execute the implementation specific code.
307 ssize_t ret = impl ().do_write (buf, nbyte);
308 if (ret >= 0)
309 {
310 impl ().offset_ += ret;
311 }
312
313#if defined(OS_TRACE_POSIX_IO_IO)
314 trace::printf ("io::%s(0x0%X, %u) @%p n=%d\n", __func__, buf, nbyte,
315 this, ret);
316#endif
317 return ret;
318 }
319
320 ssize_t
321 io::writev (const /* struct */ iovec* iov, int iovcnt)
322 {
323#if defined(OS_TRACE_POSIX_IO_IO)
324 trace::printf ("io::%s(0x0%X, %d) @%p\n", __func__, iov, iovcnt, this);
325#endif
326
327 if (iov == nullptr)
328 {
329 errno = EFAULT;
330 return -1;
331 }
332
333 if (iovcnt <= 0)
334 {
335 errno = EINVAL;
336 return -1;
337 }
338
339 if (!impl ().do_is_opened ())
340 {
341 errno = EBADF; // Not opened.
342 return -1;
343 }
344
345 if (!impl ().do_is_connected ())
346 {
347 errno = EIO; // Not opened.
348 return -1;
349 }
350
351 errno = 0;
352
353 // Execute the implementation specific code.
354 ssize_t ret = impl ().do_writev (iov, iovcnt);
355 if (ret >= 0)
356 {
357 impl ().offset_ += ret;
358 }
359 return ret;
360 }
361
362 int
363 io::fcntl (int cmd, ...)
364 {
365 // Forward to the variadic version of the function.
366 std::va_list args;
367 va_start (args, cmd);
368 int ret = vfcntl (cmd, args);
369 va_end (args);
370
371 return ret;
372 }
373
374#pragma GCC diagnostic push
375#if defined(__clang__)
376#elif defined(__GNUC__)
377#pragma GCC diagnostic ignored "-Wsuggest-final-methods"
378#endif
379 int
380 io::vfcntl (int cmd, std::va_list args)
381 {
382#if defined(OS_TRACE_POSIX_IO_IO)
383 trace::printf ("io::%s(%d) @%p\n", __func__, cmd, this);
384#endif
385
386 if (!impl ().do_is_opened ())
387 {
388 errno = EBADF; // Not opened.
389 return -1;
390 }
391
392 if (!impl ().do_is_connected ())
393 {
394 errno = EIO; // Not opened.
395 return -1;
396 }
397
398 errno = 0;
399
400 // Execute the implementation specific code.
401 return impl ().do_vfcntl (cmd, args);
402 }
403#pragma GCC diagnostic pop
404
405 int
407 {
408 errno = 0;
409
410 // Execute the implementation specific code.
411 return impl ().do_isatty ();
412 }
413
414 // fstat() on a socket returns a zero'd buffer.
415 int
416 io::fstat (struct stat* buf)
417 {
418#if defined(OS_TRACE_POSIX_IO_IO)
419 trace::printf ("io::%s(%p) @%p\n", __func__, buf, this);
420#endif
421
422 if (buf == nullptr)
423 {
424 errno = EFAULT;
425 return -1;
426 }
427
428 if (!impl ().do_is_opened ())
429 {
430 errno = EBADF; // Not opened.
431 return -1;
432 }
433
434 if (!impl ().do_is_connected ())
435 {
436 errno = EIO; // Not opened.
437 return -1;
438 }
439
440 errno = 0;
441
442 // Execute the implementation specific code.
443 return impl ().do_fstat (buf);
444 }
445
446 off_t
447 io::lseek (off_t offset, int whence)
448 {
449#if defined(OS_TRACE_POSIX_IO_IO)
450 trace::printf ("io::%s(%d, %d) @%p\n", __func__, offset, whence, this);
451#endif
452
453 if (!impl ().do_is_opened ())
454 {
455 errno = EBADF; // Not opened.
456 return -1;
457 }
458
459 errno = 0;
460
461 // Execute the implementation specific code.
462 return impl ().do_lseek (offset, whence);
463 }
464
465 // ========================================================================
466
468 {
469#if defined(OS_TRACE_POSIX_IO_IO)
470 trace::printf ("io_impl::%s()=%p\n", __func__, this);
471#endif
472 }
473
475 {
476#if defined(OS_TRACE_POSIX_IO_IO)
477 trace::printf ("io_impl::%s() @%p\n", __func__, this);
478#endif
479 }
480
481 void
483 {
484 return;
485 }
486
487#pragma GCC diagnostic push
488#if defined(__clang__)
489#pragma clang diagnostic ignored "-Wunused-parameter"
490#elif defined(__GNUC__)
491#pragma GCC diagnostic ignored "-Wunused-parameter"
492#pragma GCC diagnostic ignored "-Wsuggest-final-methods"
493#endif
494
495 bool
497 {
498 return true;
499 }
500
501 ssize_t
502 io_impl::do_writev (const /* struct */ iovec* iov, int iovcnt)
503 {
504 ssize_t total = 0;
505
506#pragma GCC diagnostic push
507#if defined(__clang__)
508#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
509#endif
510 const /* struct */ iovec* p = iov;
511 for (int i = 0; i < iovcnt; ++i, ++p)
512 {
513 ssize_t ret = do_write (p->iov_base, p->iov_len);
514 if (ret < 0)
515 {
516 return ret;
517 }
518 total += ret;
519 }
520#pragma GCC diagnostic pop
521 return total;
522 }
523
524 int
525 io_impl::do_vfcntl (int cmd, std::va_list args)
526 {
527 errno = ENOSYS; // Not implemented
528 return -1;
529 }
530
531 int
533 {
534 errno = ENOTTY; // By default, it is not a TTY.
535 return 0;
536 }
537
538 int
540 {
541 errno = ENOSYS; // Not implemented
542 return -1;
543 }
544
545#pragma GCC diagnostic pop
546
547 // ========================================================================
548 } /* namespace posix */
549} /* namespace os */
550
551// ----------------------------------------------------------------------------
static value_type * identify_device(const char *path)
Base device class.
Definition device.h:67
static int deallocate(file_descriptor_t fildes)
static file_system * identify_mounted(const char **path1, const char **path2=nullptr)
virtual bool do_is_connected(void)
Definition io.cpp:496
virtual int do_close(void)=0
virtual ssize_t do_writev(const iovec *iov, int iovcnt)
Definition io.cpp:502
virtual int do_vfcntl(int cmd, std::va_list args)
Definition io.cpp:525
virtual int do_isatty(void)
Definition io.cpp:532
virtual ssize_t do_write(const void *buf, std::size_t nbyte)=0
virtual ~io_impl()
Definition io.cpp:474
virtual void do_deallocate(void)
Definition io.cpp:482
virtual ssize_t do_read(void *buf, std::size_t nbyte)=0
virtual int do_fstat(struct stat *buf)
Definition io.cpp:539
virtual off_t do_lseek(off_t offset, int whence)=0
Base I/O class.
Definition io.h:86
int isatty(void)
Definition io.cpp:406
virtual ssize_t writev(const iovec *iov, int iovcnt)
Definition io.cpp:321
file_descriptor_t file_descriptor(void) const
Definition io.h:452
unsigned int type_t
Definition io.h:125
io(io_impl &impl, type t)
Definition io.cpp:141
virtual off_t lseek(off_t offset, int whence)
Definition io.cpp:447
io * alloc_file_descriptor(void)
Definition io.cpp:189
io_impl & impl(void) const
Definition io.h:464
virtual int fstat(struct stat *buf)
Definition io.cpp:416
virtual ssize_t write(const void *buf, std::size_t nbyte)
Definition io.cpp:268
int fcntl(int cmd,...)
Definition io.cpp:363
virtual int close(void)
Definition io.cpp:164
void clear_file_descriptor(void)
Definition io.h:446
virtual ssize_t read(void *buf, std::size_t nbyte)
Definition io.cpp:217
virtual ~io()
Definition io.cpp:152
virtual int vfcntl(int cmd, std::va_list args)
Definition io.cpp:380
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:59
io * open(const char *path, int oflag,...)
Definition io.cpp:49
io * vopen(const char *path, int oflag, std::va_list args)
Definition io.cpp:66
int stat(const char *path, struct stat *buf)
constexpr file_descriptor_t no_file_descriptor
Definition types.h:44
System namespace.
Definition uio.h:40
void * iov_base
Definition uio.h:41
size_t iov_len
Definition uio.h:42