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