diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/aconfig.h.in | 305 | ||||
-rw-r--r-- | src/check/check.c | 65 | ||||
-rw-r--r-- | src/config.h | 21 | ||||
-rw-r--r-- | src/log.c | 51 | ||||
-rw-r--r-- | src/log.h | 16 | ||||
-rw-r--r-- | src/main.c | 200 | ||||
-rw-r--r-- | src/opt.c | 54 | ||||
-rw-r--r-- | src/opt.h | 35 | ||||
-rw-r--r-- | src/status.c | 29 | ||||
-rw-r--r-- | src/status.h | 7 | ||||
-rw-r--r-- | src/ui.c | 325 | ||||
-rw-r--r-- | src/ui.h | 90 | ||||
-rw-r--r-- | src/ui_ani.c | 105 | ||||
-rw-r--r-- | src/ui_ani.h | 43 | ||||
-rw-r--r-- | src/ui_elements.c | 180 | ||||
-rw-r--r-- | src/ui_elements.h | 13 | ||||
-rw-r--r-- | src/ui_input.c | 204 | ||||
-rw-r--r-- | src/ui_input.h | 49 | ||||
-rw-r--r-- | src/ui_ipc.c | 142 | ||||
-rw-r--r-- | src/ui_ipc.h | 57 | ||||
-rw-r--r-- | src/ui_statusbar.c | 76 | ||||
-rw-r--r-- | src/ui_statusbar.h | 32 | ||||
-rw-r--r-- | src/ui_txtwindow.c | 187 | ||||
-rw-r--r-- | src/ui_txtwindow.h | 55 |
25 files changed, 2349 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..44d2220 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,8 @@ +bin_PROGRAMS=naskpass naskpass_check +naskpass_SOURCES=main.c log.c opt.c status.c ui_ani.c ui.c ui_elements.c ui_input.c ui_ipc.c ui_txtwindow.c ui_statusbar.c +naskpass_check_SOURCES=check/check.c +if DEBUG +naskpass_CFLAGS=-O0 -g3 -DDEBUG +else +naskpass_CFLAGS=-Os +endif diff --git a/src/aconfig.h.in b/src/aconfig.h.in new file mode 100644 index 0000000..8dcbf12 --- /dev/null +++ b/src/aconfig.h.in @@ -0,0 +1,305 @@ +/* src/aconfig.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the `close' function. */ +#undef HAVE_CLOSE + +/* Define to 1 if you have the `closelog' function. */ +#undef HAVE_CLOSELOG + +/* Do NOT change THIS! */ +#undef HAVE_CONFIG + +/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the <errno.h> header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `fprintf' function. */ +#undef HAVE_FPRINTF + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the <mqueue.h> header file. */ +#undef HAVE_MQUEUE_H + +/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if you have the `openlog' function. */ +#undef HAVE_OPENLOG + +/* Define to 1 if you have the `printf' function. */ +#undef HAVE_PRINTF + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#undef HAVE_REALLOC + +/* Define to 1 if you have the <semaphore.h> header file. */ +#undef HAVE_SEMAPHORE_H + +/* Define to 1 if you have the `stat' function. */ +#undef HAVE_STAT + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define to 1 if you have the <stdbool.h> header file. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlen' function. */ +#undef HAVE_STRLEN + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the `strnlen' function. */ +#undef HAVE_STRNLEN + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the <syslog.h> header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the `system' function. */ +#undef HAVE_SYSTEM + +/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the <sys/param.h> header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/wait.h> header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the <time.h> header file. */ +#undef HAVE_TIME_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the <vfork.h> header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to 1 if assertions should be disabled. */ +#undef NDEBUG + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#undef TIME_WITH_SYS_TIME + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Define if using the dmalloc debugging malloc package */ +#undef WITH_DMALLOC + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef gid_t + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `int' if <sys/types.h> does not define. */ +#undef mode_t + +/* Define to `int' if <sys/types.h> does not define. */ +#undef pid_t + +/* Define to rpl_realloc if the replacement function should be used. */ +#undef realloc + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t + +/* Define to `int' if <sys/types.h> does not define. */ +#undef ssize_t + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef uid_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/src/check/check.c b/src/check/check.c new file mode 100644 index 0000000..9d28129 --- /dev/null +++ b/src/check/check.c @@ -0,0 +1,65 @@ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <semaphore.h> +#include <mqueue.h> + + +#define myassert(x, emask) if ((x)) { ret &= ret; } else { ret |= emask; } + +static volatile unsigned long long int ret = 0x0; +static mqd_t mq_test; +static mqd_t mq_recv; +static sem_t *sp_test; +static const size_t bufsiz = 256; + +int main(int argc, char **argv) +{ + int c_stat; + struct mq_attr m_attr; + char buf[bufsiz], recv[bufsiz]; + unsigned int prio; + ssize_t sz_recv; + + memset(buf, '\0', bufsiz); + memset(recv, '\0', bufsiz); + if (argc > 1) + strncpy(buf, argv[1], bufsiz-1); + + m_attr.mq_flags = 0; + m_attr.mq_msgsize = bufsiz; + m_attr.mq_maxmsg = 10; + m_attr.mq_curmsgs = 0; + + mq_unlink("/testmq"); + myassert( (mq_test = mq_open( "/testmq", O_NONBLOCK | O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG, &m_attr )) != (mqd_t)-1, 0x1 ); + myassert( mq_getattr(mq_test, &m_attr) == 0, 0x2 ); + + strcpy(buf, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ"); + myassert( mq_send(mq_test, buf, bufsiz, 0) == 0, 0x4 ); + myassert( (sz_recv = mq_receive(mq_test, recv, bufsiz, &prio)) > 0, 0x8 ); + + memset(recv, '\0', bufsiz); + if (fork() > 0) { + myassert( (mq_recv = mq_open( "/testmq", O_RDONLY, S_IRWXU | S_IRWXG, &m_attr )) != (mqd_t)-1, 0x10 ); + myassert( (sz_recv = mq_receive(mq_recv, recv, bufsiz, &prio)) > 0, 0x20 ); + return ret; + } + myassert( mq_send(mq_test, buf, bufsiz, 0) == 0, 0x40 ); + wait(&c_stat); + myassert( c_stat == 0xFF, 0x80 ); + myassert( mq_close(mq_test) == 0, 0x100 ); + myassert( mq_unlink("/testmq") == 0, 0x200 ); + + myassert( sem_unlink("/testsem") == 0, 0x400 ); + myassert( (sp_test = sem_open("/testsem", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0)), 0x800 ); + myassert( sem_post(sp_test) == 0, 0x1000 ); + myassert( sem_wait(sp_test) == 0, 0x1200 ); + myassert( sem_close(sp_test) == 0, 0x1400 ); + myassert( sem_unlink("/testsem") == 0, 0x1800 ); + + return ret; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..797376f --- /dev/null +++ b/src/config.h @@ -0,0 +1,21 @@ +#define AUTHOR "Toni Uhlig" +#define AUTHOR_EMAIL "matzeton@googlemail.com" +#define PKGNAME "naskpass" +#define PKGDESC "A NCurses replacement for cryptsetup's askpass." +#define DEFAULT_FIFO "/lib/cryptsetup/passfifo" +#define SHTDWN_CMD "echo 'o' >/proc/sysrq-trigger" + +#define SEM_GUI "/naskpass-gui" +#define SEM_INP "/naskpass-input" +#define SEM_BSY "/naskpass-busy" +#define SEM_RDY "/naskpass-initialized" +#define MSQ_PWD "/naskpass-passwd" +#define MSQ_INF "/naskpass-info" + +#ifdef HAVE_CONFIG_H +#include "version.h" +#endif + +#ifndef VERSION +#define VERSION "unknown" +#endif diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..cd8fc7c --- /dev/null +++ b/src/log.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <syslog.h> + +#include "log.h" + +#define LOG_BUFSIZ 128 + +static FILE* logfile = NULL; + + +int log_init(char* file) +{ + if (!file) { + openlog("naskpass", LOG_NDELAY | LOG_PID, LOG_DAEMON); + return 0; + } else { + logfile = fopen(file, "a+"); + return (logfile ? 0 : errno); + } +} + +void log_free(void) +{ + if (!logfile) { + closelog(); + } else { + fclose(logfile); + logfile = NULL; + } +} + +int logs(char* format, ...) +{ + int ret; + va_list vargs; + + va_start(vargs, format); + if (!logfile) { + vsyslog(LOG_DEBUG, format, vargs); + ret = 0; + } else { + ret = vfprintf(logfile, format, vargs); + fflush(logfile); + } + va_end(vargs); + return ret; +} + diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..113141b --- /dev/null +++ b/src/log.h @@ -0,0 +1,16 @@ +#ifndef LOG_H +#define LOG_H 1 + +#ifdef DEBUG +#define logs_dbg(fmt, ...) logs(fmt, __VA_ARGS__) +#else +#define logs_dbg(fmt, ...) +#endif + +int log_init(char* file); + +void log_free(void); + +int logs(char* format, ...); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..523422f --- /dev/null +++ b/src/main.c @@ -0,0 +1,200 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <semaphore.h> +#include <time.h> +#include <mqueue.h> + +#include "config.h" +#include "opt.h" +#include "log.h" + +#include "ui.h" +#include "ui_ipc.h" +#include "ui_ani.h" +#include "ui_input.h" +#include "ui_statusbar.h" + +#define MSG(msg_idx) msg_arr[msg_idx] + + +enum msg_index { + MSG_BUSY_FD = 0, + MSG_BUSY, + MSG_NO_FIFO, + MSG_FIFO_ERR, + MSG_FIFO_BUSY, + MSG_CRYPTCMD_ERR, + MSG_NUM +}; +static const char *msg_arr[] = { "Please wait, got a piped password", + "Please wait, busy", + "check_fifo: %s is not a FIFO\n", + "check_fifo: %s error(%d): %s\n", + "fifo: cryptcreate busy", + "cryptcreate error" + }; + + +static bool +check_fifo(char *fifo_path) +{ + struct stat st; + + if (mkfifo(fifo_path, S_IRUSR | S_IWUSR) == 0) { + return (true); + } else { + if (errno == EEXIST) { + if (stat(fifo_path, &st) == 0) { + if (S_ISFIFO(st.st_mode) == 1) { + return (true); + } else { + fprintf(stderr, MSG(MSG_NO_FIFO), fifo_path); + return (false); + } + } + } + } + fprintf(stderr, MSG(MSG_FIFO_ERR), fifo_path, errno, strerror(errno)); + return (false); +} + +int +run_cryptcreate(char *pass, char *crypt_cmd) +{ + int retval; + char *cmd; + + if (crypt_cmd == NULL || pass == NULL) return (-1); + asprintf(&cmd, "echo '%s' | %s >/devnull 2>/dev/null", pass, crypt_cmd); + retval = system(cmd); + free(cmd); + return (retval); +} + +void sigfunc(int signal) +{ + switch (signal) { + case SIGTERM: + case SIGINT: + ui_ipc_semtrywait(SEM_UI); + ui_ipc_semtrywait(SEM_IN); + break; + } +} + +int +main(int argc, char **argv) +{ + int ret = EXIT_FAILURE, ffd = -1, c_status; + pid_t child; + char pbuf[IPC_MQSIZ+1]; + struct timespec ts_sem_input; + + signal(SIGINT, sigfunc); + signal(SIGTERM, sigfunc); + + if ( clock_gettime(CLOCK_REALTIME, &ts_sem_input) == -1 ) { + fprintf(stderr, "%s: clock get time error: %d (%s)\n", argv[0], errno, strerror(errno)); + goto error; + } + + if (ui_ipc_init(1) != 0) { + fprintf(stderr, "%s: can not create semaphore/message queue: %d (%s)\n", argv[0], errno, strerror(errno)); + goto error; + } + + memset(pbuf, '\0', IPC_MQSIZ+1); + if ( parse_cmd(argc, argv) != 0 ) + goto error; + log_init( GETOPT(LOG_FILE).str ); + logs("%s\n", "log init"); + logs_dbg("%s\n", "debug mode active"); + if (OPT(CRYPT_CMD).found == 0) { + fprintf(stderr, "%s: crypt cmd is mandatory\n", argv[0]); + goto error; + } + if (check_fifo(GETOPT(FIFO_PATH).str) == false) { + goto error; + } + if ((ffd = open(GETOPT(FIFO_PATH).str, O_NONBLOCK | O_RDWR)) < 0) { + fprintf(stderr, "%s: fifo '%s' error: %d (%s)\n", argv[0], GETOPT(FIFO_PATH).str, errno, strerror(errno)); + goto error; + } + + ui_ipc_sempost(SEM_UI); + if ((child = fork()) == 0) { + /* child */ + logs("%s\n", "child"); + if (ffd >= 0) close(ffd); + fclose(stderr); + /* Slave process: TUI */ + if (ui_ipc_init(0) == 0) { + ui_ipc_semwait(SEM_BS); + do_ui(); + } + exit(0); + } else if (child > 0) { + /* parent */ + logs("%s\n", "parent"); + fclose(stdin); + fclose(stdout); + /* Master process: mainloop (read passwd from message queue or fifo and exec cryptcreate */ + ui_ipc_sempost(SEM_BS); + while ( ui_ipc_getvalue(SEM_UI) > 0 ) { + logs_dbg("loop start (SEM_BS=%d, SEM_IN=%d, SEM_UI=%d).\n", ui_ipc_getvalue(SEM_BS), ui_ipc_getvalue(SEM_IN), ui_ipc_getvalue(SEM_UI)); + ui_ipc_sempost(SEM_BS); + if (read(ffd, pbuf, IPC_MQSIZ) >= 0) { + ui_ipc_msgsend(MQ_IF, MSG(MSG_BUSY_FD)); + if (run_cryptcreate(pbuf, GETOPT(CRYPT_CMD).str) != 0) { + ui_ipc_msgsend(MQ_IF, MSG(MSG_CRYPTCMD_ERR)); + } + } else if ( ui_ipc_msgcount(MQ_PW) > 0 ) { + ui_ipc_msgrecv(MQ_PW, pbuf); + logs_dbg("%s\n", "password"); + ui_ipc_msgsend(MQ_IF, MSG(MSG_BUSY)); + if (run_cryptcreate(pbuf, GETOPT(CRYPT_CMD).str) != 0) { + logs_dbg("%s\n", "cryptcreate error"); + ui_ipc_msgsend(MQ_IF, MSG(MSG_CRYPTCMD_ERR)); + } else { + logs_dbg("%s\n", "cryptcreate success, trywait SEM_UI"); + ui_ipc_semtrywait(SEM_UI); + } + logs_dbg("%s\n", "wait SEM_IN"); + ui_ipc_semwait(SEM_IN); + } + logs_dbg("%s (SEM_BS=%d)\n", "wait SEM_BS", ui_ipc_getvalue(SEM_BS)); + ui_ipc_semwait(SEM_BS); + usleep(100000); + waitpid(child, &c_status, WNOHANG); + if ( WIFEXITED(&c_status) != 0 ) { + logs("%s\n", "child exited"); + break; + } + logs_dbg("loop end (SEM_BS=%d, SEM_IN=%d, SEM_UI=%d).\n", ui_ipc_getvalue(SEM_BS), ui_ipc_getvalue(SEM_IN), ui_ipc_getvalue(SEM_UI)); + } + logs("%s\n", "waiting for child"); + wait(&c_status); + memset(pbuf, '\0', IPC_MQSIZ+1); + } else { + /* fork error */ + perror("fork"); + goto error; + } + + ret = EXIT_SUCCESS; +error: + logs("%s\n", "exiting .."); + if (ffd >= 0) close(ffd); + ui_ipc_free(1); + log_free(); + exit(ret); +} diff --git a/src/opt.c b/src/opt.c new file mode 100644 index 0000000..0c2071a --- /dev/null +++ b/src/opt.c @@ -0,0 +1,54 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include "config.h" +#include "opt.h" + +#define CONFIG_OPT(default_val) { {0},0,{default_val} } + +struct opt config_opts[] = { CONFIG_OPT(DEFAULT_FIFO), CONFIG_OPT(NULL), CONFIG_OPT(NULL) }; +const int opt_siz = ( sizeof(config_opts)/sizeof(config_opts[0]) ); + + +void +usage(char *arg0) +{ + fprintf(stderr, "\n%s (%s)\n %s\n", PKGNAME, VERSION, PKGDESC); + fprintf(stderr, " Written by %s (%s).\n", AUTHOR, AUTHOR_EMAIL); + fprintf(stderr, " License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\n"); + fprintf(stderr, " Command:\n\t%s [args]\n", arg0); + fprintf(stderr, " Arguments:\n\t-h this\n\t-l [logfile]\n\t-f [passfifo] default: %s\n\t-c [cryptcreate]\n", GETOPT(FIFO_PATH).str); +} + +int +parse_cmd(int argc, char **argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "hf:c:l::")) != -1) { + switch (opt) { + case 'h': + usage(argv[0]); + return 1; + case 'f': + SETOPT_str(FIFO_PATH, strdup(optarg)); + break; + case 'c': + SETOPT_str(CRYPT_CMD, strdup(optarg)); + break; + case 'l': + if (optarg) { + SETOPT_str(LOG_FILE, strdup(optarg)); + } else SETOPT_str(LOG_FILE, NULL); + break; + default: + usage(argv[0]); + return 1; + } + } + return 0; +} + diff --git a/src/opt.h b/src/opt.h new file mode 100644 index 0000000..57777de --- /dev/null +++ b/src/opt.h @@ -0,0 +1,35 @@ +#ifndef OPT_H +#define OPT_H 1 + +#define OPT(opt_index) config_opts[opt_index] +#define SETOPT_str(opt_index, value) { OPT(opt_index).found = 1; OPT(opt_index).opt.str = value; } +#define GETOPT(opt_index) (OPT(opt_index).found != 0 ? OPT(opt_index).opt : OPT(opt_index).def) + + +union opt_entry { + char *str; + int dec; +}; + +struct opt { + union opt_entry opt; + unsigned char found; + const union opt_entry def; +}; + +enum opt_index { + FIFO_PATH = 0, + CRYPT_CMD, + LOG_FILE +}; + + +extern struct opt config_opts[]; + +void +usage(char *arg0); + +int +parse_cmd(int argc, char **argv); + +#endif diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..5286ddf --- /dev/null +++ b/src/status.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/sysinfo.h> + +#include "status.h" + + +char * +get_system_stat(void) +{ + char *retstr = NULL; + int ncpu; + struct sysinfo inf; + + if (sysinfo(&inf) == EFAULT) { + return ("[SYSINFO ERROR]"); + } + ncpu = get_nprocs(); + + if (asprintf(&retstr, "u:%04ld - l:%3.2f,%3.2f,%3.2f - %dcore%s - mem:%lu/%lumb - procs:%02d", + inf.uptime, ((float)inf.loads[0]/10000), ((float)inf.loads[1]/10000), ((float)inf.loads[2]/10000), + ncpu, (ncpu > 1 ? "s" : ""), + (unsigned long)((inf.freeram/1024)/1024), (unsigned long)((inf.totalram/1024)/1024), inf.procs) == -1) { + return ("[ASPRINTF ERROR]"); + } + return (retstr); +} diff --git a/src/status.h b/src/status.h new file mode 100644 index 0000000..995d08a --- /dev/null +++ b/src/status.h @@ -0,0 +1,7 @@ +#ifndef STATUS_H +#define STATUS_H 1 + +char * +get_system_stat(void); + +#endif diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..360f015 --- /dev/null +++ b/src/ui.c @@ -0,0 +1,325 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <semaphore.h> +#include <string.h> +#include <ncurses.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <mqueue.h> + +#include "ui.h" +#include "ui_ipc.h" +#include "ui_elements.h" +#include "ui_ani.h" +#include "ui_input.h" +#include "ui_statusbar.h" +#include "ui_txtwindow.h" + +#include "status.h" +#include "config.h" + +#define APP_TIMEOUT 60 +#define APP_TIMEOUT_FMT "%02d" +#define PASSWD_WIDTH 35 +#define PASSWD_HEIGHT 5 +#define PASSWD_XRELPOS (unsigned int)(PASSWD_WIDTH / 2) - (PASSWD_WIDTH / 6) +#define PASSWD_YRELPOS (unsigned int)(PASSWD_HEIGHT / 2) + 1 +#define INFOWND_WIDTH 25 +#define INFOWND_HEIGHT 3 +#define INFOWND_XRELPOS (unsigned int)(INFOWND_WIDTH / 2) - (INFOWND_WIDTH / 6) +#define INFOWND_YRELPOS (unsigned int)(INFOWND_HEIGHT / 2) + 1 + +#define STRLEN(s) (sizeof(s)/sizeof(s[0])) + + +static unsigned int max_x, max_y; +static unsigned int cur_x, cur_y; +static WINDOW *wnd_main; +static struct nask_ui /* simple linked list to all UI objects */ *nui = NULL, + /* current active input */ *active = NULL; +static pthread_t thrd; +static unsigned int atmout = APP_TIMEOUT; +static pthread_cond_t cnd_update = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mtx_update = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t mtx_busy = PTHREAD_MUTEX_INITIALIZER; + + +void +register_ui_elt(struct ui_callbacks *cbs, void *data, WINDOW *wnd) +{ + struct nask_ui *tmp, *new; + + new = calloc(1, sizeof(struct nask_ui)); + new->cbs = *cbs; + new->wnd = wnd; + new->data = data; + new->next = NULL; + if (nui == NULL) { + nui = new; + nui->next = NULL; + } else { + tmp = nui; + while (tmp->next != NULL) { + tmp = tmp->next; + } + tmp->next = new; + } +} + +void +unregister_ui_elt(void *data) +{ + struct nask_ui *cur, *next, *before = NULL; + + cur = nui; + while (cur != NULL) { + next = cur->next; + if (cur->data != NULL && cur->data == data) { + free(cur); + if (before != NULL) { + before->next = next; + } else { + nui = next; + } + } + before = cur; + cur = next; + } +} + +unsigned int +ui_get_maxx(void) +{ + return max_x; +} + +unsigned int +ui_get_maxy(void) +{ + return max_y; +} + +void +ui_set_cur(unsigned int x, unsigned int y) +{ + cur_x = x; + cur_y = y; +} + +unsigned int +ui_get_curx(void) +{ + return (cur_x); +} + +unsigned int +ui_get_cury(void) +{ + return (cur_y); +} + +int +activate_ui_input(void *data) +{ + int ret = DOUI_ERR; + struct nask_ui *cur = nui; + + if (cur == NULL || data == NULL) return DOUI_NINIT; + while ( cur->data != NULL ) { + if ( cur->data == data ) { + if ( cur->cbs.ui_input != NULL && cur->cbs.ui_input(cur->wnd, data, UIKEY_ACTIVATE) == DOUI_OK ) { + active = cur; + ret = DOUI_OK; + break; + } + } + cur = cur->next; + } + return ret; +} + +int +deactivate_ui_input(void *data) +{ + int ret = DOUI_ERR; + + if (active != NULL && data == active->data) { + active = NULL; + ret = DOUI_OK; + } + return ret; +} + +static bool +process_key(char key) +{ + bool ret = false; + + if ( active != NULL ) { + ret = ( active->cbs.ui_input(active->wnd, active->data, key) == DOUI_OK ? true : false ); + } + return ret; +} + +static int +do_ui_update(bool timed_out) +{ + int retval = UICB_OK; + struct nask_ui *cur = nui; + + /* call all draw callback's */ + erase(); + if (timed_out == TRUE && atmout > 0) { + atmout--; + } else if (timed_out == TRUE && atmout == 0) { + ui_ipc_semwait(SEM_UI); + } else { + atmout = APP_TIMEOUT; + } + while (cur != NULL) { + if (cur->cbs.ui_element != NULL) { + cur->cbs.ui_element(cur->wnd, cur->data, timed_out); + doupdate(); + } else { + retval = UICB_ERR_CB; + } + cur = cur->next; + } + /* TODO: Maybe export to an extra module? */ + attron(COLOR_PAIR(1)); + mvprintw(0, max_x - STRLEN(APP_TIMEOUT_FMT), "[" APP_TIMEOUT_FMT "]", atmout); + attroff(COLOR_PAIR(1)); + /* EoT (End of Todo) */ + wmove(wnd_main, cur_y, cur_x); + wrefresh(wnd_main); + return (retval); +} + +static void * +ui_thrd(void *arg) +{ + int cnd_ret; + struct timespec now; + + do_ui_update(true); + ui_ipc_sempost(SEM_RD); + pthread_mutex_lock(&mtx_update); + clock_gettime(CLOCK_REALTIME, &now); + now.tv_sec += UILOOP_TIMEOUT; + while ( ui_ipc_getvalue(SEM_UI) > 0 ) { + cnd_ret = pthread_cond_timedwait(&cnd_update, &mtx_update, &now); + pthread_mutex_lock(&mtx_busy); + do_ui_update( (cnd_ret == ETIMEDOUT ? true : false) ); + pthread_mutex_unlock(&mtx_busy); + if (cnd_ret == ETIMEDOUT) { + clock_gettime(CLOCK_REALTIME, &now); + now.tv_sec += UILOOP_TIMEOUT; + } + } + pthread_mutex_unlock(&mtx_update); + return (NULL); +} + +void +ui_thrd_force_update(void) +{ + pthread_mutex_lock(&mtx_busy); + pthread_cond_signal(&cnd_update); + pthread_mutex_unlock(&mtx_busy); +} + +void +ui_thrd_suspend(void) +{ + pthread_mutex_lock(&mtx_busy); +} + +void +ui_thrd_resume(void) +{ + pthread_mutex_unlock(&mtx_busy); +} + +WINDOW * +init_ui(void) +{ + wnd_main = initscr(); + max_x = getmaxx(wnd_main); + max_y = getmaxy(wnd_main); + cur_x = getcurx(wnd_main); + cur_y = getcury(wnd_main); + start_color(); + init_pair(1, COLOR_RED, COLOR_WHITE); + init_pair(2, COLOR_WHITE, COLOR_BLACK); + init_pair(3, COLOR_BLACK, COLOR_WHITE); + /* TXTwindow */ + init_pair(4, COLOR_YELLOW, COLOR_RED); + init_pair(5, COLOR_WHITE, COLOR_CYAN); + /* EoF TXTwindow */ + raw(); + keypad(stdscr, TRUE); + noecho(); + nodelay(stdscr, TRUE); + cbreak(); + return (wnd_main); +} + +void +free_ui(void) +{ + delwin(wnd_main); + endwin(); + clear(); + printf(" \033[2J\n"); +} + +static int +run_ui_thrd(void) { + return (pthread_create(&thrd, NULL, &ui_thrd, NULL)); +} + +static int +stop_ui_thrd(void) +{ + return (pthread_join(thrd, NULL)); +} + +int +do_ui(void) +{ + char key = '\0'; + int ret = DOUI_ERR; + + /* init TUI and UI Elements (input field, status bar, etc) */ + init_ui(); + init_ui_elements(wnd_main, max_x, max_y); + + pthread_mutex_lock(&mtx_update); + if (run_ui_thrd() != 0) { + return ret; + } + ui_ipc_semwait(SEM_RD); + pthread_mutex_unlock(&mtx_update); + wtimeout(stdscr, 500); + while ( ui_ipc_getvalue(SEM_UI) > 0 ) { + if ((key = wgetch(wnd_main)) == ERR) { + continue; + } + if ( process_key(key) != true ) { + raise(SIGTERM); + } + ui_thrd_force_update(); + } + stop_ui_thrd(); + free_ui_elements(); + + return DOUI_OK; +} + diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..8aa1d55 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,90 @@ +#ifndef UI_H +#define UI_H 1 + +#include <ncurses.h> +#include <stdint.h> + +#define UICB_OK 0 +#define UICB_ERR_UNDEF 1 +#define UICB_ERR_CB 2 +#define UICB_ERR_BUF 3 + +#define DOUI_OK 0 +#define DOUI_ERR 1 +#define DOUI_TMOUT 2 +#define DOUI_NINIT 3 + +#define UILOOP_TIMEOUT 1 + +#define UIKEY_ACTIVATE 0 +#define UIKEY_ENTER 10 +#define UIKEY_BACKSPACE 7 +#define UIKEY_ESC 27 +#define UIKEY_DOWN 2 +#define UIKEY_UP 3 +#define UIKEY_LEFT 4 +#define UIKEY_RIGHT 5 + + +typedef int (*uicb_base)(WINDOW *, void *, bool); +typedef int (*uicb_input)(WINDOW *, void *, int); + + +struct ui_callbacks { + uicb_base ui_element; + uicb_input ui_input; +}; + +struct nask_ui { + struct ui_callbacks cbs; + WINDOW *wnd; + void *data; + struct nask_ui *next; +}; + +void +register_ui_elt(struct ui_callbacks *cbs, void *data, WINDOW *wnd); + +void +unregister_ui_elt(void *data); + +unsigned int +ui_get_maxx(void); + +unsigned int +ui_get_maxy(void); + +void +ui_set_cur(unsigned int x, unsigned int y); + +unsigned int +ui_get_curx(void); + +unsigned int +ui_get_cury(void); + +int +activate_ui_input(void *data); + +int +deactivate_ui_input(void *data); + +void +ui_thrd_force_update(void); + +void +ui_thrd_suspend(void); + +void +ui_thrd_resume(void); + +WINDOW * +init_ui(void); + +void +free_ui(void); + +int +do_ui(void); + +#endif diff --git a/src/ui_ani.c b/src/ui_ani.c new file mode 100644 index 0000000..d1f1073 --- /dev/null +++ b/src/ui_ani.c @@ -0,0 +1,105 @@ +#include <stdlib.h> +#include <string.h> + +#include "ui.h" +#include "ui_ani.h" + +#define ANIC_INITSTATE '|' + + +struct anic * +init_anic_default(unsigned int x, unsigned int y, chtype attrs, char *fmt) +{ + struct anic *a = init_anic(x, y, attrs, anic_cb); + struct anic_default *b = calloc(1, sizeof(struct anic_default)); + + a->data = (void *) b; + b->state = ANIC_INITSTATE; + if (fmt != NULL) { + b->fmt = strdup(fmt); + } + return (a); +} + +struct anic * +init_anic(unsigned int x, unsigned int y, chtype attrs, uicb_anic uicb) +{ + struct anic *a = calloc(1, sizeof(struct anic)); + + a->x = x; + a->y = y; + a->uicb = uicb; + a->attrs = attrs; + return (a); +} + +void +free_anic(struct anic *a) +{ + free(a); +} + +void +free_anic_default(struct anic *a) +{ + struct anic_default *b; + + if (a->data != NULL) { + b = (struct anic_default *) a->data; + free(b->fmt); + free(b); + } + free_anic(a); +} + +int +anic_cb(WINDOW *win, void *data, bool timed_out) +{ + struct anic *a = (struct anic *) data; + struct anic_default *b; + char *tmp; + int retval = UICB_OK; + + if (a == NULL) return (UICB_ERR_UNDEF); + b = (struct anic_default *) a->data; + if (timed_out == true) { + switch (b->state) { + default: + case '|': b->state = '/'; break; + case '/': b->state = '-'; break; + case '-': b->state = '\\'; break; + case '\\': b->state = '|'; break; + } + } + attron(a->attrs); + if (b->fmt != NULL) { + if (asprintf(&tmp, b->fmt, b->state) <= 0) { + retval = UICB_ERR_BUF; + } + } else { + asprintf(&tmp, "%c", b->state); + } + if (win != NULL) { + mvwprintw(win, a->y, a->x, tmp); + } else { + mvprintw(a->y, a->x, tmp); + } + free(tmp); + attroff(a->attrs); + return (retval); +} + +void +register_anic(struct anic *a, uicb_anic uicb) +{ + struct ui_callbacks cbs; + cbs.ui_element = uicb; + cbs.ui_input = NULL; + register_ui_elt(&cbs, (void *) a, NULL); +} + +void +register_anic_default(struct anic *a) +{ + register_anic(a, anic_cb); +} diff --git a/src/ui_ani.h b/src/ui_ani.h new file mode 100644 index 0000000..3d6ece2 --- /dev/null +++ b/src/ui_ani.h @@ -0,0 +1,43 @@ +#ifndef UI_ANIC_H +#define UI_ANIC_H 1 + +#include <ncurses.h> + + +typedef int (*uicb_anic)(WINDOW *, void *, bool); + +struct anic_default { + char state; + char *fmt; +}; + +struct anic { + unsigned int x; + unsigned int y; + uicb_anic uicb; + void *data; + chtype attrs; +}; + +struct anic * +init_anic_default(unsigned int x, unsigned int y, chtype attrs, char *fmt); + +struct anic * +init_anic(unsigned int x, unsigned int y, chtype attrs, uicb_anic uicb); + +void +free_anic_default(struct anic *a); + +void +free_anic(struct anic *a); + +int +anic_cb(WINDOW *win, void *data, bool timed_out); + +void +register_anic(struct anic *a, uicb_anic uicb); + +void +register_anic_default(struct anic *a); + +#endif diff --git a/src/ui_elements.c b/src/ui_elements.c new file mode 100644 index 0000000..f072d45 --- /dev/null +++ b/src/ui_elements.c @@ -0,0 +1,180 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "ui.h" +#include "ui_ipc.h" +#include "ui_ani.h" +#include "ui_input.h" +#include "ui_statusbar.h" +#include "ui_txtwindow.h" +#include "ui_elements.h" + +#include "status.h" + +#define PASSWD_WIDTH 35 +#define PASSWD_HEIGHT 5 +#define PASSWD_XRELPOS (unsigned int)(PASSWD_WIDTH / 2) - (PASSWD_WIDTH / 6) +#define PASSWD_YRELPOS (unsigned int)(PASSWD_HEIGHT / 2) + 1 +#define INFOWND_WIDTH 25 +#define INFOWND_HEIGHT 1 +#define BSTR_LEN 3 + +static struct input *pw_input; +static struct anic *heartbeat; +static struct statusbar *higher, *lower; +static struct txtwindow *infownd; +static struct tctwindow *errwnd; +static char *title = NULL; +static char busy_str[BSTR_LEN+1] = ".\0\0\0"; + + +static int +lower_statusbar_update(WINDOW *win, struct statusbar *bar, bool ui_timeout) +{ + if (ui_timeout == FALSE) return DOUI_OK; + char *tmp = get_system_stat(); + set_statusbar_text(bar, tmp); + free(tmp); + return DOUI_OK; +} + +static int +higher_statusbar_update(WINDOW *win, struct statusbar *bar, bool ui_timeout) +{ + return DOUI_OK; +} + +static int +infownd_update(WINDOW *win, struct txtwindow *tw, bool ui_timeout) +{ + if (ui_timeout == TRUE && tw->active == TRUE) { + size_t len = strlen(busy_str); + if (len > BSTR_LEN) { + memset(busy_str, '\0', BSTR_LEN+1); + busy_str[0] = '.'; + } else strcat(busy_str, "."); + } + return DOUI_OK; +} + +static int +passwd_input_cb(WINDOW *wnd, void *data, int key) +{ + struct input *a = (struct input *) data; + char ipc_buf[IPC_MQSIZ+1]; + + memset(ipc_buf, '\0', IPC_MQSIZ+1); +// wtimeout(stdscr, -1); + switch (key) { + case UIKEY_ENTER: + ui_ipc_msgsend(MQ_PW, a->input); + + ui_thrd_suspend(); + clear_input(wnd, a); + deactivate_input(pw_input); + ui_thrd_resume(); + + ui_ipc_msgrecv(MQ_IF, ipc_buf); + + ui_thrd_suspend(); + set_txtwindow_color(infownd, COLOR_PAIR(5), COLOR_PAIR(5)); + set_txtwindow_title(infownd, "BUSY"); + set_txtwindow_text(infownd, ipc_buf); + set_txtwindow_active(infownd, true); + ui_thrd_resume(); + ui_thrd_force_update(); + + sleep(2); + + if (ui_ipc_msgcount(MQ_IF) > 0) { + ui_ipc_msgrecv(MQ_IF, ipc_buf); + ui_thrd_suspend(); + set_txtwindow_color(infownd, COLOR_PAIR(4), COLOR_PAIR(4) | A_BOLD); + set_txtwindow_title(infownd, "ERROR"); + set_txtwindow_text(infownd, ipc_buf); + ui_thrd_resume(); + while (wgetch(stdscr) != '\n') { }; + } + + ui_thrd_suspend(); + set_txtwindow_active(infownd, false); + activate_input(pw_input); + ui_thrd_resume(); + + ui_ipc_sempost(SEM_IN); + break; + case UIKEY_BACKSPACE: + del_input(wnd, a); + break; + case UIKEY_ESC: + wtimeout(stdscr, 0); + ui_thrd_suspend(); + deactivate_input(pw_input); + set_txtwindow_active(infownd, true); + set_txtwindow_color(infownd, COLOR_PAIR(5), COLOR_PAIR(5)); + set_txtwindow_title(infownd, "BUSY"); + set_txtwindow_text(infownd, "bye bye"); + ui_thrd_resume(); + ui_thrd_force_update(); + sleep(2); + return DOUI_ERR; + case UIKEY_DOWN: + case UIKEY_UP: + case UIKEY_LEFT: + case UIKEY_RIGHT: + break; + case UIKEY_ACTIVATE: + break; + default: + add_input(wnd, a, key); + } +// wtimeout(stdscr, 1000); + refresh(); + return DOUI_OK; +} + +void +init_ui_elements(WINDOW *wnd_main, unsigned int max_x, unsigned int max_y) +{ + asprintf(&title, "/* %s-%s */", PKGNAME, VERSION); + pw_input = init_input((unsigned int)(max_x / 2)-PASSWD_XRELPOS, + (unsigned int)(max_y / 2)-PASSWD_YRELPOS, + PASSWD_WIDTH, "PASSWORD: ", + IPC_MQSIZ, COLOR_PAIR(3), COLOR_PAIR(2)); + heartbeat = init_anic_default(0, 0, A_BOLD | COLOR_PAIR(1), "[%c]"); + higher = init_statusbar(0, max_x, A_BOLD | COLOR_PAIR(3), + higher_statusbar_update); + lower = init_statusbar(max_y - 1, max_x, COLOR_PAIR(3), + lower_statusbar_update); + infownd = init_txtwindow_centered(INFOWND_WIDTH, INFOWND_HEIGHT, + infownd_update); + + register_input(NULL, pw_input, passwd_input_cb); + register_statusbar(higher); + register_statusbar(lower); + register_anic_default(heartbeat); + register_txtwindow(infownd); + activate_input(pw_input); + set_statusbar_text(higher, title); +} + +void +free_ui_elements(void) +{ + unregister_ui_elt(lower); + unregister_ui_elt(higher); + unregister_ui_elt(heartbeat); + unregister_ui_elt(pw_input); + free_input(pw_input); + free_anic_default(heartbeat); + free_statusbar(higher); + free_statusbar(lower); + free_txtwindow(infownd); + free_ui(); + if (title) { + free(title); + title = NULL; + } +} diff --git a/src/ui_elements.h b/src/ui_elements.h new file mode 100644 index 0000000..0cf8826 --- /dev/null +++ b/src/ui_elements.h @@ -0,0 +1,13 @@ +#ifndef UI_ELEMENTS_H +#define UI_ELEMENTS_H 1 + +#include "config.h" + + +void +init_ui_elements(WINDOW *wnd_main, unsigned int max_x, unsigned int max_y); + +void +free_ui_elements(void); + +#endif diff --git a/src/ui_input.c b/src/ui_input.c new file mode 100644 index 0000000..9c53330 --- /dev/null +++ b/src/ui_input.c @@ -0,0 +1,204 @@ +#include <stdlib.h> +#include <string.h> + +#include "ui.h" +#include "ui_input.h" + + +struct input * +init_input(unsigned int x, unsigned int y, unsigned int width, char *prompt, size_t input_len, chtype attrs, chtype shadow) +{ + struct input *a = calloc(1, sizeof(struct input)); + + a->x = x; + a->y = y; + a->width = width; + a->cur_pos = 0; + a->input = calloc(input_len+1, sizeof(char)); + a->input_max = input_len; + a->input_len = 0; + a->input_pos = 0; + a->prompt = strdup(prompt); + a->attrs = attrs; + a->shadow = shadow; + return (a); +} + +void +free_input(struct input *a) +{ + if (a->input != NULL) { + free(a->input); + } + free(a->prompt); + free(a); +} + +static void +print_wnd(size_t addwidth, struct input *a) +{ + int i, x = a->x, y = a->y; + size_t relwidth = addwidth*2, len = strlen(a->prompt) + a->width; + char tmp[len+relwidth+1]; + + attron(a->attrs); + memset(tmp, ' ', len+relwidth); + tmp[len+relwidth] = '\0'; + for (i = -1; i <= 1; i++) + mvprintw(y+i, x-addwidth, tmp); + + mvhline(y-2, x-addwidth, 0, len+relwidth); + mvhline(y+2, x-addwidth, 0, len+relwidth); + mvvline(y-1, x-addwidth-1, 0, 3); + mvvline(y-1, x+len+addwidth, 0, 3); + mvaddch(y-2, x-addwidth-1, ACS_ULCORNER); + mvaddch(y+2, x-addwidth-1, ACS_LLCORNER); + mvaddch(y-2, x+len+addwidth, ACS_URCORNER); + mvaddch(y+2, x+len+addwidth, ACS_LRCORNER); + attroff(a->attrs); + + attron(a->shadow); + for (i = x-addwidth+1; i < x+len+relwidth; i++) + mvaddch(y+3, i, ACS_CKBOARD); + for (i = -1; i < 3; i++) { + mvaddch(y+i, x+len+relwidth-2, ACS_CKBOARD); + mvaddch(y+i, x+len+relwidth-1, ACS_CKBOARD); + } + attroff(a->shadow); +} + +static void +print_input_text(WINDOW *win, struct input *a) +{ + size_t start = 0; + size_t p_len = strlen(a->prompt); + + char tmp[a->width + 1]; + memset(tmp, '\0', a->width + 1); + if (a->input_pos >= a->width) { + start = a->input_pos - a->width; + } + + strncpy(tmp, (char *)(a->input + start), a->width); + int i; + for (i = 0; i < strlen(tmp); i++) { + tmp[i] = '*'; + } + + if (win == NULL) { + mvprintw(a->y, a->x + p_len, "%s", tmp); + } else { + mvwprintw(win, a->y, a->x + p_len, "%s", tmp); + } +} + +static void +print_input(WINDOW *win, struct input *a) +{ + char *tmp; + int i; + size_t p_len = strlen(a->prompt); + + print_wnd(3, a); + attron(a->attrs); + if (win) { + mvwprintw(win, a->y, a->x, a->prompt); + } else { + mvprintw(a->y, a->x, a->prompt); + } + tmp = calloc(a->width+1, sizeof(char)); + for (i = 0; i < a->width; i++) { + *(tmp + i) = '_'; + } + if (win) { + mvwprintw(win, a->y, a->x + p_len, tmp); + } else { + mvprintw(a->y, a->x + p_len, tmp); + } + free(tmp); + print_input_text(win, a); + attroff(a->attrs); +} + +int +activate_input(struct input *a) +{ + if (a == NULL) return (UICB_ERR_UNDEF); + curs_set(1); + ui_set_cur(a->x + strlen(a->prompt) + a->cur_pos, a->y); + return (activate_ui_input( (void *) a )); +} + +int +deactivate_input(struct input *a) +{ + curs_set(0); + return (deactivate_ui_input(a)); +} + +int +add_input(WINDOW *win, struct input *a, int key) +{ + if (a == NULL) return (UICB_ERR_UNDEF); + if (a->input_len >= a->input_max) return (UICB_ERR_BUF); + *(a->input + a->input_pos) = (char) key; + ++a->input_pos; + ++a->input_len; + a->cur_pos = (a->cur_pos+1 < a->width ? a->cur_pos+1 : a->cur_pos); + ui_set_cur(a->x + strlen(a->prompt) + a->cur_pos, a->y); + return (UICB_OK); +} + +int +del_input(WINDOW *win, struct input *a) +{ + if (a == NULL) return (UICB_ERR_UNDEF); + if (a->input_len == 0) return (UICB_ERR_BUF); + memmove((a->input + a->input_pos - 1), (a->input + a->input_pos), a->input_max - a->input_pos); + --a->input_len; + *(a->input + a->input_len) = '\0'; + if (a->input_pos-1 == a->input_len) { + --a->input_pos; + } + if (a->cur_pos+1 < a->width && a->cur_pos > 0) { + --a->cur_pos; + } else if (a->cur_pos-1 == a->input_pos) { + --a->cur_pos; + } + mvwprintw(win, a->y, a->x + a->cur_pos + strlen(a->prompt), "_"); + ui_set_cur(a->x + strlen(a->prompt) + a->cur_pos, a->y); + return (UICB_OK); +} + +int +clear_input(WINDOW *win, struct input *a) +{ + if (a == NULL) return (UICB_ERR_UNDEF); + memset(a->input, '\0', a->input_max); + a->input_len = 0; + a->input_pos = 0; + a->cur_pos = 0; + ui_set_cur(a->x + strlen(a->prompt) + a->cur_pos, a->y); + return (UICB_OK); +} + +static int +input_cb(WINDOW *win, void *data, bool timed_out) +{ + struct input *a = (struct input *) data; + + if (a == NULL) return (UICB_ERR_UNDEF); + if (win != NULL && is_wintouched(win) == false) return (UICB_OK); + print_input(win, a); + return (UICB_OK); +} + +void +register_input(WINDOW *win, struct input *a, uicb_input ipcb) +{ + struct ui_callbacks cbs; + cbs.ui_element = input_cb; + cbs.ui_input = ipcb; + register_ui_elt(&cbs, (void *) a, win); +} + diff --git a/src/ui_input.h b/src/ui_input.h new file mode 100644 index 0000000..df7088c --- /dev/null +++ b/src/ui_input.h @@ -0,0 +1,49 @@ +#ifndef UI_INPUT_H +#define UI_INPUT_H 1 + +#include <ncurses.h> + + +struct input { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int cur_pos; + char *input; + size_t input_max; + size_t input_len; + size_t input_pos; + char *prompt; + chtype attrs; + chtype shadow; + uicb_input cb_input; +}; + +struct input * +init_input(unsigned int x, unsigned int y, unsigned int width, char *prompt, size_t input_len, chtype attrs, chtype shadow); + +void +free_input(struct input *a); + +int +activate_input(struct input *a); + +int +deactivate_input(struct input *a); + +int +add_input(WINDOW *win, struct input *a, int key); + +int +del_input(WINDOW *win, struct input *a); + +int +clear_input(WINDOW *win, struct input *a); + +void +register_input(WINDOW *win, struct input *a, uicb_input ipcb); + +void +unregister_input(struct input *a); + +#endif diff --git a/src/ui_ipc.c b/src/ui_ipc.c new file mode 100644 index 0000000..cf137e7 --- /dev/null +++ b/src/ui_ipc.c @@ -0,0 +1,142 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef SEM_TIMEDWAIT +#include <time.h> +#endif +#include <semaphore.h> +#include <mqueue.h> +#include <sys/stat.h> +#include <alloca.h> +#include <errno.h> + +#include "ui_ipc.h" + +#define JMP_IF(cmd, retval, jmplabel) if ( (cmd) == retval ) { printf("(%s) == %p\n", #cmd, (void*)retval); goto jmplabel; } + + +static sem_t *sems[SEM_NUM]; +static mqd_t msqs[MSQ_NUM]; + + +int +ui_ipc_init(int is_master) +{ + volatile int sp_oflags, mq_oflags; + mode_t crt_flags; + struct mq_attr m_attr; + + bzero(sems, sizeof(sem_t*)*SEM_NUM); + bzero(msqs, sizeof(mqd_t)*MSQ_NUM); + m_attr.mq_flags = 0; + m_attr.mq_msgsize = IPC_MQSIZ; + m_attr.mq_maxmsg = 3; + m_attr.mq_curmsgs = 0; + if (is_master) { + sp_oflags = O_CREAT | O_EXCL; + mq_oflags = O_NONBLOCK | O_CREAT | O_EXCL; + crt_flags = S_IRUSR | S_IWUSR; + JMP_IF( msqs[MQ_PW] = mq_open(MSQ_PWD, mq_oflags | O_RDONLY, crt_flags, &m_attr), (mqd_t)-1, error ); + JMP_IF( msqs[MQ_IF] = mq_open(MSQ_INF, mq_oflags | O_WRONLY, crt_flags, &m_attr), (mqd_t)-1, error ); + } else { + sp_oflags = 0; + mq_oflags = 0; + crt_flags = 0; + JMP_IF( msqs[MQ_PW] = mq_open(MSQ_PWD, mq_oflags | O_WRONLY, crt_flags, &m_attr), (mqd_t)-1, error ); + JMP_IF( msqs[MQ_IF] = mq_open(MSQ_INF, mq_oflags | O_RDONLY, crt_flags, &m_attr), (mqd_t)-1, error ); + } + JMP_IF( sems[SEM_UI] = sem_open(SEM_GUI, sp_oflags, crt_flags, 0), SEM_FAILED, error ); + JMP_IF( sems[SEM_IN] = sem_open(SEM_INP, sp_oflags, crt_flags, 0), SEM_FAILED, error ); + JMP_IF( sems[SEM_BS] = sem_open(SEM_BSY, sp_oflags, crt_flags, 0), SEM_FAILED, error ); + JMP_IF( sems[SEM_RD] = sem_open(SEM_RDY, sp_oflags, crt_flags, 0), SEM_FAILED, error ); + return 0; +error: + return errno; +} + +void +ui_ipc_free(int is_master) +{ + int i; + + for (i = 0; i < SEM_NUM; i++) { + if (sems[i]) sem_close(sems[i]); + } + for (i = 0; i < MSQ_NUM; i++) { + if (msqs[i]) mq_close(msqs[i]); + } + if (is_master > 0) { + sem_unlink(SEM_BSY); + sem_unlink(SEM_GUI); + sem_unlink(SEM_INP); + sem_unlink(SEM_RDY); + mq_unlink(MSQ_PWD); + mq_unlink(MSQ_INF); + } +} + +int +ui_ipc_sempost(enum UI_IPC_SEM e_sp) +{ + return ( sem_post(sems[e_sp]) ); +} + +int +ui_ipc_semwait(enum UI_IPC_SEM e_sp) +{ + return ( sem_wait(sems[e_sp]) ); +} + +int +ui_ipc_semtrywait(enum UI_IPC_SEM e_sp) +{ + return ( sem_trywait(sems[e_sp]) ); +} + +int +ui_ipc_getvalue(enum UI_IPC_SEM e_sp) +{ + int sp_val = 0; + + if (sem_getvalue(sems[e_sp], &sp_val) != 0) { + return -1; + } + return sp_val; +} + +#ifdef SEM_TIMEDWAIT +int +ui_ipc_semtimedwait(enum UI_IPC_SEM e_sp, int timeout) +{ + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + return -1; + } + ts.tc_sec += timeout; + return ( sem_timedwait(sems[q_mq], &ts) ); +} +#endif + +int +ui_ipc_msgsend(enum UI_IPC_MSQ e_mq, const char *msg_ptr) +{ + char *tmp = alloca(IPC_MQSIZ); + memset(tmp, '\0', IPC_MQSIZ); + strncpy(tmp, msg_ptr, IPC_MQSIZ); + return ( mq_send(msqs[e_mq], tmp, IPC_MQSIZ, 0) ); +} + +ssize_t +ui_ipc_msgrecv(enum UI_IPC_MSQ e_mq, char *msg_ptr) +{ + return mq_receive(msqs[e_mq], msg_ptr, IPC_MQSIZ, NULL); +} + +long +ui_ipc_msgcount(enum UI_IPC_MSQ e_mq) +{ + struct mq_attr m_attr; + bzero(&m_attr, sizeof(struct mq_attr)); + if (mq_getattr(msqs[e_mq], &m_attr) != 0) return -1; + return m_attr.mq_curmsgs; +} diff --git a/src/ui_ipc.h b/src/ui_ipc.h new file mode 100644 index 0000000..5ecfaa4 --- /dev/null +++ b/src/ui_ipc.h @@ -0,0 +1,57 @@ +#ifndef UI_IPC_H +#define UI_IPC_H 1 + +#include "status.h" +#include "config.h" + +#define IPC_MQSIZ 128 + + +enum UI_IPC_SEM { + SEM_RD = 0, /* UI Init done? */ + SEM_UI, /* TUI active? */ + SEM_IN, /* Textfield has input avail */ + SEM_BS, /* Master process busy */ + SEM_NUM +}; + +enum UI_IPC_MSQ { + MQ_PW = 0, + MQ_IF, + MSQ_NUM +}; + + +int +ui_ipc_init(int is_master); + +void +ui_ipc_free(int is_master); + +int +ui_ipc_sempost(enum UI_IPC_SEM e_sp); + +int +ui_ipc_semwait(enum UI_IPC_SEM e_sp); + +int +ui_ipc_semtrywait(enum UI_IPC_SEM e_sp); + +int +ui_ipc_getvalue(enum UI_IPC_SEM e_sp); + +#ifdef SEM_TIMEDWAIT +int +ui_ipc_semtimedwait(enum UI_IPC_MSQ e_sp, int timeout); +#endif + +int +ui_ipc_msgsend(enum UI_IPC_MSQ e_mq, const char *msg_ptr); + +ssize_t +ui_ipc_msgrecv(enum UI_IPC_MSQ e_mq, char *msg_ptr); + +long +ui_ipc_msgcount(enum UI_IPC_MSQ e_mq); + +#endif diff --git a/src/ui_statusbar.c b/src/ui_statusbar.c new file mode 100644 index 0000000..df88683 --- /dev/null +++ b/src/ui_statusbar.c @@ -0,0 +1,76 @@ +#include <stdlib.h> +#include <string.h> + +#include "ui.h" +#include "ui_statusbar.h" + + +struct statusbar * +init_statusbar(unsigned int y, unsigned int width, chtype attrs, status_func cb_update) +{ + struct statusbar *a = calloc(1, sizeof(struct statusbar)); + + a->y = y; + a->width = width; + a->text = calloc(a->width, sizeof(char)); + a->attrs = attrs; + a->status_func = cb_update; + return (a); +} + +void +free_statusbar(struct statusbar *a) +{ + if (a->text) { + free(a->text); + } + free(a); +} + +int +statusbar_cb(WINDOW *win, void *data, bool timed_out) +{ + struct statusbar *a = (struct statusbar *) data; + char *tmp; + unsigned int diff_pos = 0; + size_t len; + + if (a == NULL) return (UICB_ERR_UNDEF); + attron(a->attrs); + len = strnlen(a->text, a->width); + if (len < a->width) { + diff_pos = (unsigned int) (a->width - len)/2; + } + tmp = (char *) malloc(a->width + 1); + memset(tmp, ' ', a->width); + tmp[a->width] = '\0'; + strncpy((tmp + diff_pos), a->text, len); + if (a->status_func != NULL) { + a->status_func(win, a, timed_out); + } + if (win != NULL) { + mvwprintw(win, a->y, 0, tmp); + } else { + mvprintw(a->y, 0, tmp); + } + free(tmp); + attroff(a->attrs); + return (UICB_OK); +} + +void +register_statusbar(struct statusbar *a) +{ + struct ui_callbacks cbs; + cbs.ui_element = statusbar_cb; + cbs.ui_input = NULL; + register_ui_elt(&cbs, (void *) a, NULL); +} + +inline void +set_statusbar_text(struct statusbar *a, const char *text) +{ + size_t len = strlen(text); + + strncpy(a->text, text, (len > a->width ? a->width : len)); +} diff --git a/src/ui_statusbar.h b/src/ui_statusbar.h new file mode 100644 index 0000000..65e9e12 --- /dev/null +++ b/src/ui_statusbar.h @@ -0,0 +1,32 @@ +#ifndef UI_STATUSBAR_H +#define UI_STATUSBAR_H 1 + +#include <ncurses.h> + + +struct statusbar { + unsigned int y; + unsigned int width; + char *text; + int (*status_func)(WINDOW *, struct statusbar *, bool); + chtype attrs; +}; + +typedef int (*status_func)(WINDOW *, struct statusbar *, bool); + +struct statusbar * +init_statusbar(unsigned int y, unsigned int width, chtype attrs, status_func cb_update); + +void +free_statusbar(struct statusbar *a); + +int +statusbar_cb(WINDOW *win, void *data, bool timed_out); + +void +register_statusbar(struct statusbar *a); + +void +set_statusbar_text(struct statusbar *a, const char *text); + +#endif diff --git a/src/ui_txtwindow.c b/src/ui_txtwindow.c new file mode 100644 index 0000000..90d152d --- /dev/null +++ b/src/ui_txtwindow.c @@ -0,0 +1,187 @@ +#include <stdlib.h> +#include <string.h> + +#include "ui_txtwindow.h" + + +struct txtwindow * +init_txtwindow(unsigned int x, unsigned int y, unsigned int width, unsigned int height, window_func cb_update) +{ + struct txtwindow *a = calloc(1, sizeof(struct txtwindow)); + + a->x = x; + a->y = y; + a->width = width; + a->height = height; + a->active = false; + a->title_len = INITIAL_TITLE_LEN; + a->title = calloc(a->title_len+1, sizeof(char)); + a->text = NULL; + a->attrs = 0; + a->text_attrs = 0; + a->window_func = cb_update; + return (a); +} + +struct txtwindow * +init_txtwindow_centered(unsigned int width, unsigned int height, window_func cb_update) +{ + unsigned int x = (ui_get_maxx()/2)-(width/2); + unsigned int y = (ui_get_maxy()/2)-(height/2); + return init_txtwindow(x, y, width, height, cb_update); +} + +static void +__free_text(struct txtwindow *a) +{ + if (a->text) { + if (a->text[0]) { + free(a->text[0]); + } + free(a->text); + a->text = NULL; + } +} + +void +free_txtwindow(struct txtwindow *a) +{ + __free_text(a); + if (a->title) { + free(a->title); + } + free(a); +} + +static void +print_wnd(struct txtwindow *a) +{ + int i, x = a->x, y = a->y, w = a->width, h = a->height; + char tmp[a->width+1]; + + attron(a->attrs); + /* print window surface */ + memset(tmp, ' ', a->width); + tmp[a->width] = '\0'; + for (i = y-1; i < y+h+1; i++) + mvprintw(i, x, tmp); + /* print window border */ + mvhline(y-2, x, 0, w); + mvhline(y+h+1, x, 0, w); + mvvline(y-1, x-1, 0, h+3); + mvvline(y-1, x+w, 0, h+3); + /* print window border edges */ + mvaddch(y-2, x-1, ACS_ULCORNER); + mvaddch(y+1+h, x-1, ACS_LLCORNER); + mvaddch(y-2, x+w, ACS_URCORNER); + mvaddch(y+1+h, x+w, ACS_LRCORNER); + /* print window title */ + attroff(a->attrs); + attron(a->text_attrs); + mvprintw(y-2, x+(w/2)-((a->title_len+4)/2), "[ %s ]", a->title); + /* print windows text */ + i = -1; + if (a->text) { + while ( a->text[++i] ) { + mvprintw(y+i, x+1, a->text[i]); + } + } + attroff(a->text_attrs); +} + +static int +txtwindow_cb(WINDOW *win, void *data, bool timedout) +{ + struct txtwindow *a = (struct txtwindow *) data; + + if (a->active == true) { + print_wnd(a); + if (a->window_func) { + attron(a->text_attrs); + a->window_func(win, a, timedout); + attroff(a->text_attrs); + } + } + return (UICB_OK); +} + +void inline +register_txtwindow(struct txtwindow *a) +{ + struct ui_callbacks cbs; + cbs.ui_element = txtwindow_cb; + cbs.ui_input = NULL; + register_ui_elt(&cbs, (void *) a, NULL); +} + +static size_t +__do_textcpy(char **p_dest, size_t sz_dest, const char *p_src, size_t sz_src) +{ + size_t cursiz = sz_dest; + + if (sz_src > sz_dest) { + *p_dest = (char *) realloc(*p_dest, (sz_src+1) * sizeof(char)); + cursiz = sz_src; + } + memset(*p_dest, '\0', (cursiz+1) * sizeof(char)); + memcpy(*p_dest, p_src, (cursiz+1) * sizeof(char)); + return sz_src; +} + +/* seperate a String with NEWLINES into an array */ +static char ** +__do_textadjust(struct txtwindow *a, char *text) +{ + int i = 0, rows = (int)(strlen(text) / a->width); + char **adj_text = calloc(rows+2, sizeof(char *)); + char *p_strtok, *tok; + const char sep[] = "\n"; + + if (rows > a->height) goto error; + p_strtok = strdup(text); + do { + tok = strsep(&p_strtok, sep); + if (strlen(tok) > a->width) { + strcpy(tok+a->width-3, "..."); + *(tok+a->width) = '\0'; + } + adj_text[i] = tok; + i++; + } while (rows > 0); + return adj_text; +error: + free(adj_text); + return NULL; +} + +void +set_txtwindow_text(struct txtwindow *a, char *text) +{ + char **fmt_text = __do_textadjust(a, text); + + if (fmt_text) { + __free_text(a); + a->text = fmt_text; + } +} + +void +set_txtwindow_title(struct txtwindow *a, const char *title) +{ + a->title_len = __do_textcpy(&a->title, a->title_len, title, strlen(title)); +} + +void +set_txtwindow_color(struct txtwindow *a, chtype wnd, chtype txt) +{ + a->attrs = wnd; + a->text_attrs = txt; +} + +void +set_txtwindow_dim(struct txtwindow *a, unsigned int w, unsigned int h) +{ + a->width = w; + a->height = h; +} + diff --git a/src/ui_txtwindow.h b/src/ui_txtwindow.h new file mode 100644 index 0000000..1e1bc3f --- /dev/null +++ b/src/ui_txtwindow.h @@ -0,0 +1,55 @@ +#ifndef UI_TXTWINDOW_H +#define UI_TXTWINDOW_H 1 + +#include <ncurses.h> + +#include "ui.h" + +#define INITIAL_TITLE_LEN 32 + +#define set_txtwindow_active(wnd, activate) wnd->active = activate; + +struct txtwindow { + unsigned int y; + unsigned int x; + unsigned int width; + unsigned int height; + bool active; + char *title; + size_t title_len; + char **text; + int (*window_func)(WINDOW *, struct txtwindow *, bool); + chtype attrs; + chtype text_attrs; +}; + +typedef int (*window_func)(WINDOW *, struct txtwindow *, bool); + +struct txtwindow * +init_txtwindow(unsigned int x, unsigned int y, unsigned int width, unsigned int height, window_func cb_update); + +struct txtwindow * +init_txtwindow_centered(unsigned int width, unsigned int height, window_func cb_update); + +void +free_txtwindow(struct txtwindow *a); + +void +register_txtwindow(struct txtwindow *a); + +void +set_txtwindow_text(struct txtwindow *a, char *text); + +void +set_txtwindow_title(struct txtwindow *a, const char *title); + +void +set_txtwindow_color(struct txtwindow *a, chtype wnd, chtype txt); + +void +set_txtwindow_pos(struct txtwindow *a, unsigned int x, unsigned int y); + +void +set_txtwindow_dim(struct txtwindow *a, unsigned int w, unsigned int h); + +#endif |