diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 5d79be5..725be32 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -91,6 +91,8 @@ extern "C" { #include <3ds/font.h> #include <3ds/mii.h> +#include <3ds/gdbhio_dev.h> + #ifdef __cplusplus } #endif diff --git a/libctru/include/3ds/gdbhio.h b/libctru/include/3ds/gdbhio.h new file mode 100644 index 0000000..1b0656a --- /dev/null +++ b/libctru/include/3ds/gdbhio.h @@ -0,0 +1,29 @@ +/** + * @file gdbhio.h + * @brief Luma3DS GDB HIO (called File I/O in GDB documentation) functions. + */ + +#pragma once + +#include +#include +#include + +#define GDBHIO_STDIN_FILENO 0 +#define GDBHIO_STDOUT_FILENO 1 +#define GDBHIO_STDERR_FILENO 2 + +int gdbHioOpen(const char *pathname, int flags, mode_t mode); +int gdbHioClose(int fd); +int gdbHioRead(int fd, void *buf, unsigned int count); +int gdbHioWrite(int fd, const void *buf, unsigned int count); +off_t gdbHioLseek(int fd, off_t offset, int flag); +int gdbHioRename(const char *oldpath, const char *newpath); +int gdbHioUnlink(const char *pathname); +int gdbHioStat(const char *pathname, struct stat *st); +int gdbHioFstat(int fd, struct stat *st); +int gdbHioGettimeofday(struct timeval *tv, void *tz); +int gdbHioIsatty(int fd); + +///< Host I/O 'system' function, requires 'set remote system-call-allowed 1'. +int gdbHioSystem(const char *command); diff --git a/libctru/include/3ds/gdbhio_dev.h b/libctru/include/3ds/gdbhio_dev.h new file mode 100644 index 0000000..44fd5dc --- /dev/null +++ b/libctru/include/3ds/gdbhio_dev.h @@ -0,0 +1,37 @@ +/** + * @file gdbhio_dev.h + * @brief Luma3DS GDB HIO (called File I/O in GDB documentation) devoptab wrapper. + */ + +#pragma once + +#include + +struct timeval; + +///< Initializes the GDB HIO devoptab wrapper, returns 0 on success, -1 on failure. +int gdbHioDevInit(void); + +///< Deinitializes the GDB HIO devoptab wrapper. +void gdbHioDevExit(void); + +///< Returns a file descriptor mapping to the GDB client console's standard input stream. +int gdbHioDevGetStdin(void); + +///< Returns a file descriptor mapping to the GDB client console's standard output stream. +int gdbHioDevGetStdout(void); + +///< Returns a file descriptor mapping to the GDB client console's standard error stream. +int gdbHioDevGetStderr(void); + +///< Redirects 0 to 3 of the application's standard streams to GDB client console's. Returns -1, -2, or -3, resp., on failure; 0 on success. +int gdbHioDevRedirectStdStreams(bool in, bool out, bool err); + +///< GDB HIO POSIX function gettimeofday. +int gdbHioDevGettimeofday(struct timeval *tv, void *tz); + +///< GDB HIO POSIX function isatty. +int gdbHioDevIsatty(int fd); + +///< GDB HIO POSIX function system. Requires 'set remote system-call-allowed 1'. +int gdbHioDevSystem(const char *command); diff --git a/libctru/source/gdbhio.c b/libctru/source/gdbhio.c new file mode 100644 index 0000000..8796370 --- /dev/null +++ b/libctru/source/gdbhio.c @@ -0,0 +1,403 @@ +#include <3ds/gdbhio.h> + +#include <3ds/svc.h> +#include <3ds/result.h> + +#include +#include + +#include +#include + +#define GDBHIO_O_RDONLY 0x0 +#define GDBHIO_O_WRONLY 0x1 +#define GDBHIO_O_RDWR 0x2 +#define GDBHIO_O_ACCMODE 0x3 +#define GDBHIO_O_APPEND 0x8 +#define GDBHIO_O_CREAT 0x200 +#define GDBHIO_O_TRUNC 0x400 +#define GDBHIO_O_EXCL 0x800 +#define GDBHIO_O_SUPPORTED (GDBHIO_O_RDONLY | GDBHIO_O_WRONLY| \ + GDBHIO_O_RDWR | GDBHIO_O_APPEND| \ + GDBHIO_O_CREAT | GDBHIO_O_TRUNC| \ + GDBHIO_O_EXCL) + +#define GDBHIO_S_IFREG 0100000 +#define GDBHIO_S_IFDIR 040000 +#define GDBHIO_S_IFCHR 020000 +#define GDBHIO_S_IRUSR 0400 +#define GDBHIO_S_IWUSR 0200 +#define GDBHIO_S_IXUSR 0100 +#define GDBHIO_S_IRWXU (GDBHIO_S_IRUSR | GDBHIO_S_IWUSR | GDBHIO_S_IXUSR) +#define GDBHIO_S_IRGRP 040 +#define GDBHIO_S_IWGRP 020 +#define GDBHIO_S_IXGRP 010 +#define GDBHIO_S_IRWXG (GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IXGRP) +#define GDBHIO_S_IROTH 04 +#define GDBHIO_S_IWOTH 02 +#define GDBHIO_S_IXOTH 01 +#define GDBHIO_S_IRWXO (GDBHIO_S_IROTH | GDBHIO_S_IWOTH | GDBHIO_S_IXOTH) +#define GDBHIO_S_SUPPORTED (GDBHIO_S_IFREG|GDBHIO_S_IFDIR| \ + GDBHIO_S_IRWXU|GDBHIO_S_IRWXG| \ + GDBHIO_S_IRWXO) + +#define GDBHIO_SEEK_SET 0 +#define GDBHIO_SEEK_CUR 1 +#define GDBHIO_SEEK_END 2 + +#define GDBHIO_EPERM 1 +#define GDBHIO_ENOENT 2 +#define GDBHIO_EINTR 4 +#define GDBHIO_EIO 5 +#define GDBHIO_EBADF 9 +#define GDBHIO_EACCES 13 +#define GDBHIO_EFAULT 14 +#define GDBHIO_EBUSY 16 +#define GDBHIO_EEXIST 17 +#define GDBHIO_ENODEV 19 +#define GDBHIO_ENOTDIR 20 +#define GDBHIO_EISDIR 21 +#define GDBHIO_EINVAL 22 +#define GDBHIO_ENFILE 23 +#define GDBHIO_EMFILE 24 +#define GDBHIO_EFBIG 27 +#define GDBHIO_ENOSPC 28 +#define GDBHIO_ESPIPE 29 +#define GDBHIO_EROFS 30 +#define GDBHIO_ENOSYS 88 +#define GDBHIO_ENAMETOOLONG 91 +#define GDBHIO_EUNKNOWN 9999 + +typedef struct PackedGdbHioRequest +{ + char magic[4]; // "GDB\x00" + u32 version; + + // Request + char functionName[16+1]; + char paramFormat[8+1]; + + u64 parameters[8]; + size_t stringLengths[8]; + + // Return + s64 retval; + int gdbErrno; + bool ctrlC; +} PackedGdbHioRequest; + +static __thread bool g_gdbHioWasInterruptedByCtrlC = false; + +bool gdbHioWasInterruptedByCtrlC(void) +{ + return g_gdbHioWasInterruptedByCtrlC; +} + +static int _gdbHioImportErrno(int errnum) +{ + switch (errnum) { + case GDBHIO_EPERM: return EPERM; + case GDBHIO_ENOENT: return ENOENT; + case GDBHIO_EINTR: return EINTR; + case GDBHIO_EIO: return EIO; + case GDBHIO_EBADF: return EBADF; + case GDBHIO_EACCES: return EACCES; + case GDBHIO_EFAULT: return EFAULT; + case GDBHIO_EBUSY: return EBUSY; + case GDBHIO_EEXIST: return EEXIST; + case GDBHIO_ENODEV: return ENODEV; + case GDBHIO_ENOTDIR: return ENOTDIR; + case GDBHIO_EISDIR: return EISDIR; + case GDBHIO_EINVAL: return EINVAL; + case GDBHIO_ENFILE: return ENFILE; + case GDBHIO_EMFILE: return EMFILE; + case GDBHIO_EFBIG: return EFBIG; + case GDBHIO_ENOSPC: return ENOSPC; + case GDBHIO_ESPIPE: return ESPIPE; + case GDBHIO_EROFS: return EROFS; + case GDBHIO_ENOSYS: return ENOSYS; + case GDBHIO_ENAMETOOLONG: return ENAMETOOLONG; + default: return EPIPE; + } +} + +static int _gdbHioExportOpenFlags(int flags) +{ + int outflags = 0; + if (flags & O_CREAT) outflags |= GDBHIO_O_CREAT; + if (flags & O_EXCL) outflags |= GDBHIO_O_EXCL; + if (flags & O_TRUNC) outflags |= GDBHIO_O_TRUNC; + if (flags & O_APPEND) outflags |= GDBHIO_O_APPEND; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + outflags |= GDBHIO_O_RDONLY; + break; + case O_WRONLY: + outflags |= GDBHIO_O_WRONLY; + break; + case O_RDWR: + outflags |= GDBHIO_O_RDWR; + break; + default: + break; + } + + // Note: O_BINARY is implicit if the host supports it + + return outflags; +} + +typedef s32 gdbhio_mode_t; + +static mode_t _gdbHioImportFileMode(gdbhio_mode_t gdbMode) +{ + mode_t mode = 0; + if (mode & ~GDBHIO_S_SUPPORTED) return -1; + + if (gdbMode & GDBHIO_S_IFREG) mode |= S_IFREG; + if (gdbMode & GDBHIO_S_IFDIR) mode |= S_IFDIR; + if (gdbMode & GDBHIO_S_IFCHR) mode |= S_IFCHR; + if (gdbMode & GDBHIO_S_IRUSR) mode |= S_IRUSR; + if (gdbMode & GDBHIO_S_IWUSR) mode |= S_IWUSR; + if (gdbMode & GDBHIO_S_IXUSR) mode |= S_IXUSR; + if (gdbMode & GDBHIO_S_IRGRP) mode |= S_IRGRP; + if (gdbMode & GDBHIO_S_IWGRP) mode |= S_IWGRP; + if (gdbMode & GDBHIO_S_IXGRP) mode |= S_IXGRP; + if (gdbMode & GDBHIO_S_IROTH) mode |= S_IROTH; + if (gdbMode & GDBHIO_S_IWOTH) mode |= S_IWOTH; + if (gdbMode & GDBHIO_S_IXOTH) mode |= S_IXOTH; + + return mode; +} + +static int _gdbHioExportFileMode(mode_t mode) +{ + gdbhio_mode_t gdbMode = 0; + if (mode & ~GDBHIO_S_SUPPORTED) return -1; + + if (mode & S_IFREG) gdbMode |= GDBHIO_S_IFREG; + if (mode & S_IFDIR) gdbMode |= GDBHIO_S_IFDIR; + if (mode & S_IFCHR) gdbMode |= GDBHIO_S_IFCHR; + if (mode & S_IRUSR) gdbMode |= GDBHIO_S_IRUSR; + if (mode & S_IWUSR) gdbMode |= GDBHIO_S_IWUSR; + if (mode & S_IXUSR) gdbMode |= GDBHIO_S_IXUSR; + if (mode & S_IRGRP) gdbMode |= GDBHIO_S_IRGRP; + if (mode & S_IWGRP) gdbMode |= GDBHIO_S_IWGRP; + if (mode & S_IXGRP) gdbMode |= GDBHIO_S_IXGRP; + if (mode & S_IROTH) gdbMode |= GDBHIO_S_IROTH; + if (mode & S_IWOTH) gdbMode |= GDBHIO_S_IWOTH; + if (mode & S_IXOTH) gdbMode |= GDBHIO_S_IXOTH; + + return mode; +} + + +static int _gdbExportSeekFlag(int flag) +{ + switch (flag) { + case SEEK_SET: return GDBHIO_SEEK_SET; + case SEEK_CUR: return GDBHIO_SEEK_CUR; + case SEEK_END: return GDBHIO_SEEK_END; + default: return GDBHIO_SEEK_SET; + } +} +// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat +typedef u32 gdbhio_time_t; + +struct PACKED ALIGN(4) gdbhio_stat { + u32 st_dev; /* device */ + u32 st_ino; /* inode */ + gdbhio_mode_t st_mode; /* protection */ + u32 st_nlink; /* number of hard links */ + u32 st_uid; /* user ID of owner */ + u32 st_gid; /* group ID of owner */ + u32 st_rdev; /* device type (if inode device) */ + u64 st_size; /* total size, in bytes */ + u64 st_blksize; /* blocksize for filesystem I/O */ + u64 st_blocks; /* number of blocks allocated */ + gdbhio_time_t st_atime; /* time of last access */ + gdbhio_time_t st_mtime; /* time of last modification */ + gdbhio_time_t st_ctime; /* time of last change */ +}; + +static inline u32 _gdbHioImportScalar32(u32 v) +{ + return __builtin_bswap32(v); +} + +static inline u64 _gdbHioImportScalar64(u64 v) +{ + return __builtin_bswap64(v); +} + +static void _gdbHioImportStructStat(struct stat *out, const struct gdbhio_stat *in) +{ + memset(out, 0, sizeof(struct stat)); + out->st_dev = _gdbHioImportScalar32(in->st_dev); + out->st_ino = _gdbHioImportScalar32(in->st_ino); + out->st_mode = _gdbHioImportFileMode(_gdbHioImportScalar32(in->st_mode)); + out->st_nlink = _gdbHioImportScalar32(in->st_nlink); + out->st_uid = _gdbHioImportScalar32(in->st_uid); + out->st_gid = _gdbHioImportScalar32(in->st_gid); + out->st_rdev = _gdbHioImportScalar32(in->st_rdev); + out->st_size = (off_t)_gdbHioImportScalar64(in->st_size); + out->st_blksize = (blksize_t)_gdbHioImportScalar64(in->st_blksize); + out->st_blocks = (blkcnt_t)_gdbHioImportScalar64(in->st_blocks); + out->st_atime = _gdbHioImportScalar32(in->st_atime); + out->st_mtime = _gdbHioImportScalar32(in->st_mtime); + out->st_ctime = _gdbHioImportScalar32(in->st_ctime); +} + +struct gdbhio_timeval { + gdbhio_time_t tv_sec; /* second */ + u64 tv_usec; /* microsecond */ +}; + +static void _gdbHioImportStructTimeval(struct timeval *out, const struct gdbhio_timeval *in) +{ + out->tv_sec = _gdbHioImportScalar32(in->tv_sec); + out->tv_usec = _gdbHioImportScalar64(in->tv_usec); +} + +static void _gdbHioSetErrno(int gdbErrno, bool ctrlC) +{ + if (gdbErrno != 0) { + errno = _gdbHioImportErrno(gdbErrno); + } + g_gdbHioWasInterruptedByCtrlC = ctrlC; +} + +static s64 _gdbHioSendSyncRequest64V(const char *name, const char *paramFormat, va_list args) +{ + PackedGdbHioRequest req = {{0}}; + memcpy(req.magic, "GDB", 4); + strncpy(req.functionName, name, 16); + strncpy(req.paramFormat, paramFormat, 8); + + u32 numStrs = 0; + const char *str; + + for (u32 i = 0; i < 8 && paramFormat[i] != 0; i++) { + switch (paramFormat[i]) { + case 'i': + case 'I': + req.parameters[i] = va_arg(args, u32); + break; + case 'l': + case 'L': + req.parameters[i] = va_arg(args, u64); + break; + case 'p': + req.parameters[i] = va_arg(args, uintptr_t); + break; + case 's': + str = va_arg(args, const char *); + req.parameters[i] = (uintptr_t)str; + req.stringLengths[numStrs++] = strlen(str)+1; + break; + default: + break; + } + } + + if (R_FAILED(svcOutputDebugString((const char *)&req, 0)) || req.paramFormat[0] != 0) { + errno = EPIPE; + g_gdbHioWasInterruptedByCtrlC = false; + return -1; + } + + _gdbHioSetErrno(req.gdbErrno, req.ctrlC); + return req.retval; +} + +static s64 _gdbHioSendSyncRequest64(const char *name, const char *paramFormat, ...) +{ + s64 ret = 0; + va_list args; + va_start(args, paramFormat); + ret = _gdbHioSendSyncRequest64V(name, paramFormat, args); + va_end(args); + return ret; +} + +static int _gdbHioSendSyncRequest(const char *name, const char *paramFormat, ...) +{ + s64 ret = 0; + va_list args; + va_start(args, paramFormat); + ret = _gdbHioSendSyncRequest64V(name, paramFormat, args); + va_end(args); + return (int)ret; +} + +int gdbHioOpen(const char *pathname, int flags, mode_t mode) +{ + return _gdbHioSendSyncRequest("open", "siI", pathname, _gdbHioExportOpenFlags(flags), _gdbHioExportFileMode(mode)); +} + +int gdbHioClose(int fd) +{ + return _gdbHioSendSyncRequest("close", "i", fd); +} + +int gdbHioRead(int fd, void *buf, unsigned int count) +{ + return _gdbHioSendSyncRequest("read", "ipI", fd, buf, count); +} + +int gdbHioWrite(int fd, const void *buf, unsigned int count) +{ + return _gdbHioSendSyncRequest("write", "ipI", fd, buf, count); +} + +off_t gdbHioLseek(int fd, off_t offset, int flag) +{ + return _gdbHioSendSyncRequest64("lseek", "ili", fd, offset, _gdbExportSeekFlag(flag)); +} + +int gdbHioRename(const char *oldpath, const char *newpath) +{ + return _gdbHioSendSyncRequest("rename", "ss", oldpath, newpath); +} + +int gdbHioUnlink(const char *pathname) +{ + return _gdbHioSendSyncRequest("unlink", "s", pathname); +} + +int gdbHioStat(const char *pathname, struct stat *st) +{ + struct gdbhio_stat gst; + int ret = _gdbHioSendSyncRequest("stat", "sp", pathname, &gst); + _gdbHioImportStructStat(st, &gst); + return ret; +} + +int gdbHioFstat(int fd, struct stat *st) +{ + struct gdbhio_stat gst; + int ret = _gdbHioSendSyncRequest("fstat", "ip", fd, &gst); + _gdbHioImportStructStat(st, &gst); + return ret; +} + +int gdbHioGettimeofday(struct timeval *tv, void *tz) +{ + // GDB ignores tz and passes NULL + struct gdbhio_timeval gtv; + int ret = _gdbHioSendSyncRequest("gettimeofday", "pp", >v, tz); + _gdbHioImportStructTimeval(tv, >v); + return ret; +} + +int gdbHioIsatty(int fd) +{ + return _gdbHioSendSyncRequest("isatty", "i", fd); +} + +// Requires set remote system-call-allowed 1 +int gdbHioSystem(const char *command) +{ + return _gdbHioSendSyncRequest("system", "s", command); +} diff --git a/libctru/source/gdbhio_dev.c b/libctru/source/gdbhio_dev.c new file mode 100644 index 0000000..a286e43 --- /dev/null +++ b/libctru/source/gdbhio_dev.c @@ -0,0 +1,250 @@ +#include <3ds/gdbhio_dev.h> +#include <3ds/gdbhio.h> + +#include +#include + +#include +#include + +static int g_gdbHioStdinFd = -1, g_gdbHioStdoutFd = -1, g_gdbHioStderrFd = -1; + +static int _gdbHioGetFd(int fd) +{ + __handle *handle = __get_handle(fd); + if (handle == NULL) { + errno = EBADF; + return -1; + } + + if(strcmp(devoptab_list[handle->device]->name, "gdbhio") != 0) { + errno = EBADF; + return -1; + } + return *(int *)handle->fileStruct; +} + +static inline int _gdbHioGetFdFromPtr(void *fdptr) +{ + return *(int *)fdptr; +} + +static inline const char *_gdbHioSkipMountpoint(const char *pathname) +{ + return strncmp(pathname, "gdbhio:", 7) == 0 ? pathname + 7 : pathname; +} + +static int _gdbHioDevOpen(struct _reent *r, void *fdptr, const char *pathname, int flags, int mode) +{ + (void)r; + + pathname = _gdbHioSkipMountpoint(pathname); + int ret = gdbHioOpen(pathname, flags, mode); + if (ret < 0) { + return ret; + } else { + *(int *)fdptr = ret; + return 0; + } +} + +static int _gdbHioDevClose(struct _reent *r, void *fdptr) +{ + (void)r; + + int fd = _gdbHioGetFdFromPtr(fdptr); + if (fd == g_gdbHioStdinFd) + g_gdbHioStdinFd = -1; + else if (fd == g_gdbHioStdoutFd) + g_gdbHioStdoutFd = -1; + else if (fd == g_gdbHioStderrFd) + g_gdbHioStderrFd = -1; + else + return gdbHioClose(fd); + + return 0; +} + +static ssize_t _gdbHioDevRead(struct _reent *r, void *fdptr, char *buf, size_t count) +{ + (void)r; + return gdbHioRead(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count); +} + +static ssize_t _gdbHioDevWrite(struct _reent *r, void *fdptr, const char *buf, size_t count) +{ + (void)r; + return gdbHioWrite(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count); +} + +static off_t _gdbHioDevLseek(struct _reent *r, void *fdptr, off_t offset, int flag) +{ + (void)r; + return gdbHioLseek(_gdbHioGetFdFromPtr(fdptr), offset, flag); +} + +static int _gdbHioDevRename(struct _reent *r, const char *oldpath, const char *newpath) +{ + (void)r; + return gdbHioRename(_gdbHioSkipMountpoint(oldpath), _gdbHioSkipMountpoint(newpath)); +} + +static int _gdbHioDevUnlink(struct _reent *r, const char *pathname) +{ + (void)r; + return gdbHioUnlink(_gdbHioSkipMountpoint(pathname)); +} + +static int _gdbHioDevStat(struct _reent *r, const char *pathname, struct stat *st) +{ + (void)r; + return gdbHioStat(_gdbHioSkipMountpoint(pathname), st); +} + +static int _gdbHioDevFstat(struct _reent *r, void *fdptr, struct stat *st) +{ + (void)r; + return gdbHioFstat(_gdbHioGetFdFromPtr(fdptr), st); +} + +static int _gdbHioDevImportFd(int gdbFd) +{ + int fd, dev; + + dev = FindDevice("gdbhio:"); + if(dev == -1) + return -1; + + fd = __alloc_handle(dev); + if(fd == -1) + return -1; + + *(int *)__get_handle(fd)->fileStruct = gdbFd; + + return fd; +} + +static const devoptab_t g_gdbHioDevoptab = { + .name = "gdbhio", + .structSize = sizeof(int), + .open_r = _gdbHioDevOpen, + .close_r = _gdbHioDevClose, + .write_r = _gdbHioDevWrite, + .read_r = _gdbHioDevRead, + .seek_r = _gdbHioDevLseek, + .fstat_r = _gdbHioDevFstat, + .stat_r = _gdbHioDevStat, + .link_r = NULL, + .unlink_r = _gdbHioDevUnlink, + .chdir_r = NULL, + .rename_r = _gdbHioDevRename, + .mkdir_r = NULL, + .dirStateSize = 0, + .diropen_r = NULL, + .dirreset_r = NULL, + .dirnext_r = NULL, + .dirclose_r = NULL, + .statvfs_r = NULL, + .ftruncate_r = NULL, + .fsync_r = NULL, + .deviceData = 0, + .chmod_r = NULL, + .fchmod_r = NULL, + .rmdir_r = NULL, +}; + +int gdbHioDevGettimeofday(struct timeval *tv, void *tz) +{ + return gdbHioGettimeofday(tv, tz); +} + +int gdbHioDevIsatty(int fd) +{ + return gdbHioIsatty(_gdbHioGetFd(fd)); +} + +int gdbHioDevSystem(const char *command) +{ + return gdbHioSystem(command); +} + +int gdbHioDevInit(void) +{ + int dev = FindDevice("gdbhio:"); + if (dev >= 0) { + return -1; + } + + dev = AddDevice(&g_gdbHioDevoptab); + if (dev < 0) return -1; + + return 0; +} + +void gdbHioDevExit(void) +{ + close(g_gdbHioStdinFd); + close(g_gdbHioStdoutFd); + close(g_gdbHioStderrFd); + RemoveDevice("gdbhio:"); +} + +int gdbHioDevGetStdin(void) +{ + if (g_gdbHioStdinFd < 0) + g_gdbHioStdinFd = _gdbHioDevImportFd(GDBHIO_STDIN_FILENO); + + return g_gdbHioStdinFd; +} + +int gdbHioDevGetStdout(void) +{ + if (g_gdbHioStdoutFd < 0) + g_gdbHioStdoutFd = _gdbHioDevImportFd(GDBHIO_STDOUT_FILENO); + + return g_gdbHioStdoutFd; +} + +int gdbHioDevGetStderr(void) +{ + if (g_gdbHioStderrFd < 0) + g_gdbHioStderrFd = _gdbHioDevImportFd(GDBHIO_STDERR_FILENO); + + return g_gdbHioStderrFd; +} + +int gdbHioDevRedirectStdStreams(bool in, bool out, bool err) +{ + int ret = 0; + if (in) { + if (gdbHioDevGetStdin() < 0) return -1; + ret = dup2(g_gdbHioStdinFd, STDIN_FILENO); + if (ret < 0) return -1; + if (ret != g_gdbHioStdinFd) { + close(g_gdbHioStdinFd); + g_gdbHioStdinFd = STDIN_FILENO; + } + } + + if (out) { + if (gdbHioDevGetStdout() < 0) return -2; + ret = dup2(g_gdbHioStdoutFd, STDOUT_FILENO); + if (ret < 0) return -2; + if (ret != g_gdbHioStdoutFd) { + close(g_gdbHioStdoutFd); + g_gdbHioStdoutFd = STDOUT_FILENO; + } + } + + if (err) { + if (gdbHioDevGetStderr() < 0) return -3; + ret = dup2(g_gdbHioStderrFd, STDERR_FILENO); + if (ret < 0) return -3; + if (ret != g_gdbHioStderrFd) { + close(g_gdbHioStderrFd); + g_gdbHioStderrFd = STDERR_FILENO; + } + } + + return 0; +}