Skip to content

Instantly share code, notes, and snippets.

@xelxebar
Last active August 29, 2015 14:19
Show Gist options
  • Save xelxebar/6e4dbb946009324df79e to your computer and use it in GitHub Desktop.
Save xelxebar/6e4dbb946009324df79e to your computer and use it in GitHub Desktop.
fcat: A file union server
/* fcat: A file union server
*
* fcat <pipe_path> [<file 1> [<file 2> [...]]]
*
* This little program creates a pipe at pipe_path which acts like a file with
* the concatenated contents of a list o files.
*
* For debugging and troubleshooting purposes a small logging system is also
* provided.
*
*
* Copyright (C) 2015 Wilson, Brandon
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <errno.h>
#define unused(arg) ((void)arg)
#define BUF_CPY_SZ ((ssize_t)4096)
#define LOG_DEF_SZ ((ssize_t)8)
#define LOG_MSG_MAX_SZ ((ssize_t)1024)
#define LOG_PREFIX_DEF "(--)"
#define LOG_PREFIX_ERROR "(EE)"
#define LOG_PREFIX_INFO "(II)"
#define LOG_PREFIX_DEBUG "(DD)"
#define X_LOG_FUN(x_type,X_TYPE)
#define X_LOG_FUNS \
X_LOG_FUN(error, MSG_ERROR) \
X_LOG_FUN(info, MSG_INFO) \
X_LOG_FUN(debug, MSG_DEBUG)
#define msg_type_cmp(type1, type2) ((bool)(type1 & type2))
typedef struct logmsg t_logmsg;
typedef struct log t_log;
typedef enum msg_type t_msg_type;
char *realpath(const char *path, char *resolved_path);
static t_log *deflog;
/*** Logging ***/
struct log {
t_logmsg **msgs;
ssize_t msg_cnt;
ssize_t size;
};
enum msg_type {
MSG_NONE = 0,
MSG_ERROR = (0x1<<0),
MSG_INFO = (0x1<<1),
MSG_DEBUG = (0x1<<2),
MSG_ALL = (-1)
};
struct logmsg {
int id;
char *loc;
char *msg;
t_msg_type type;
};
t_log *init_log() {
t_log *log = calloc(1, sizeof(t_log));
t_logmsg **logmsgs = calloc(LOG_DEF_SZ, sizeof(t_logmsg *));
log->msgs = logmsgs;
log->msg_cnt = 0;
log->size = LOG_DEF_SZ;
return log;
}
void log_deinit(t_log *log) {
int i;
for (i = 0; i < log->msg_cnt; i++) {
free(log->msgs[i]->loc);
free(log->msgs[i]->msg);
free(log->msgs[i]);
}
free(log->msgs);
free(log);
}
void commit_logmsg(t_log *log, t_logmsg *logmsg) {
if (log->size == log->msg_cnt) {
ssize_t new_size = log->size + LOG_DEF_SZ;
t_logmsg **new_msgs = realloc(log->msgs, new_size * sizeof(t_logmsg *));
log->msgs = new_msgs;
log->size = new_size;
}
log->msgs[log->msg_cnt] = logmsg;
log->msg_cnt += 1;
}
void log_vmsg(t_log *log, t_msg_type type, int id,
const char *loc, const char *msg , va_list ap) {
t_logmsg *logmsg = calloc(1, sizeof(t_logmsg));
int loc_len = strlen(loc);
char *ploc = calloc(loc_len + 1, sizeof(char));
char *pmsg = calloc(LOG_MSG_MAX_SZ, sizeof(char));
strncpy(ploc, loc, loc_len);
vsnprintf(pmsg, LOG_MSG_MAX_SZ - 1, msg, ap);
logmsg->id = id;
logmsg->loc = ploc;
logmsg->msg = pmsg;
logmsg->type = type;
commit_logmsg(log, logmsg);
}
void log_msg(t_log *log, t_msg_type type, int id,
const char *loc, const char *msg, ...) {
va_list ap;
va_start(ap, msg);
log_vmsg(log, type, id, loc, msg, ap);
va_end(ap);
}
// Generate helper functions log_X to log errors, info, etc
#define X_LOG_FUN(x_type, X_TYPE) \
void log_##x_type(t_log *log, int id, \
const char *loc, const char *msg, ...) { \
va_list ap; \
va_start(ap, msg); \
log_vmsg(log, X_TYPE, id, loc, msg, ap); \
va_end(ap); \
}
X_LOG_FUNS
#undef X_LOG_FUN
void log_tnprint(t_log *log, int cnt, t_msg_type type) {
int i = 0;
if (cnt < 0) i = 0;
else i = log->msg_cnt - cnt;
for (i; i < log->msg_cnt; i++) {
t_logmsg *logmsg = log->msgs[i];
char *prefix;
FILE *outfile = stdout;
if (!msg_type_cmp(logmsg->type, type)) continue;
switch (logmsg->type) {
case MSG_ERROR: prefix = LOG_PREFIX_ERROR;
outfile = stderr; break;
case MSG_INFO: prefix = LOG_PREFIX_INFO; break;
case MSG_DEBUG: prefix = LOG_PREFIX_DEBUG; break;
default: prefix = LOG_PREFIX_DEF; break;
}
fprintf(outfile, "%s %i %s: %s\n", prefix, logmsg->id, logmsg->loc, logmsg->msg);
}
}
void log_print(t_log *log) {
log_tnprint(log, -1, MSG_ALL);
}
/*** File Union ***/
int cat(int outfd, int infd) {
char *log_loc = "cat";
char *buf = calloc(BUF_CPY_SZ, sizeof(char));
ssize_t inbc = 0;
while ((inbc = pread(infd, buf, BUF_CPY_SZ - 1, inbc)) != -1 ) {
ssize_t outbc = write(outfd, buf, inbc);
if (inbc != outbc) {
log_error(deflog, errno, log_loc,
"Unable to complete write to %i(fd) from %i(fd)", outfd, infd);
return -1;
} else {
buf = memset(buf, 0x0, BUF_CPY_SZ);
if (inbc < BUF_CPY_SZ - 1) break;
}
}
free(buf);
if (inbc == -1) {
log_error(deflog, errno, log_loc, "Read of fd %i failed", infd);
return -1;
}
return 0;
}
int acat(int outfd, char **fns, int fncnt) {
char *log_loc = "acat";
int i;
for (i = 0; i < fncnt; i++) {
char *fn = *(fns + i);
int fd;
if ((fd = open(fn, O_RDONLY)) == -1) {
log_error(deflog, errno, log_loc, "Failed to read file %s", fn);
return -1;
}
if (cat(outfd, fd) == -1) {
log_error(deflog, errno, log_loc,
"Concat of %s to fd %i failed", fn, outfd);
return -1;
}
if (close(fd) == -1) {
log_error(deflog, errno, log_loc, "Failed to close fd: %i", fd);
return -1;
}
}
return 0;
}
int resolve_path(char **canon_path, const char *fn, int amode) {
char *log_loc = "resolve_path";
int ok = access(fn, amode) == 0;
if (! ok && errno == ENOENT) {
log_error(deflog, errno, log_loc, "No such file: %s", fn);
return -1;
} else if (! ok && errno != ENOENT) {
log_error(deflog, errno, log_loc, "File access error: %s", fn);
return -1;
} else if ((*canon_path = realpath(fn, NULL)) == NULL) {
log_error(deflog, errno, log_loc, "Could not get full path: %s", fn);
return -1;
}
return 0;
}
int main(int argc, char **argv) {
static char *log_loc = "main";
int ret = 0;
int path_cnt = argc - 1;
char **paths = calloc(path_cnt, sizeof(char *));
char *fifofn = argv[1];
int fifo;
int fncnt = argc - 2;
char **fns;
deflog = init_log();
int ok = (mkfifo(fifofn, 0644) != -1);
if (! ok && errno == EEXIST) {
log_info(deflog, errno, log_loc,
"Found existing fifo %s; continuing.", fifofn);
} else if (! ok && errno != EEXIST) {
log_error(deflog, errno, log_loc, "Error creating fifo %s", fifofn);
ret = -1;
goto cleanup;
}
int i;
for (i = 0; i < path_cnt; i++) {
char *fn = argv[i+1];
int amode = i == 0 ? W_OK : R_OK;
if (resolve_path(&paths[i], fn, amode) == -1) {
log_error(deflog, errno, log_loc,
"Error resolving path for file: %s", fn);
ret = -1;
goto cleanup;
}
}
fifofn = paths[0];
fns = paths + 1;
int pcnt = 0;
while ((fifo = open(fifofn, O_WRONLY)) != -1 ) {
if (acat(fifo, fns, fncnt) == -1) {
log_error(deflog, errno, log_loc, "Error concatenating files");
ret = -1;
goto cleanup;
}
if (close(fifo) == -1) {
log_error(deflog, errno, log_loc, "Error closing fifo %i", fifo);
ret = -1;
goto cleanup;
}
if (deflog->msg_cnt > pcnt) {
log_tnprint(deflog, deflog->msg_cnt - pcnt, MSG_ALL);
pcnt = deflog->msg_cnt;
}
sync();
}
if (fifo == -1) {
log_error(deflog, errno, log_loc, "Error opening fifo %s", fifofn);
ret = -1;
goto cleanup;
}
cleanup:
log_print(deflog);
log_deinit(deflog);
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment