int tube_write_msg(struct tube* tube, uint8_t* buf, uint32_t len, int nonblock) { ssize_t r, d; int fd = tube->sw; /* test */ if(nonblock) { r = write(fd, &len, sizeof(len)); if(r == -1) { if(errno==EINTR || errno==EAGAIN) return -1; log_err("tube msg write failed: %s", strerror(errno)); return -1; /* can still continue, perhaps */ } } else r = 0; if(!fd_set_block(fd)) return 0; /* write remainder */ d = r; while(d != (ssize_t)sizeof(len)) { if((r=write(fd, ((char*)&len)+d, sizeof(len)-d)) == -1) { if(errno == EAGAIN) continue; /* temporarily unavail: try again*/ log_err("tube msg write failed: %s", strerror(errno)); (void)fd_set_nonblock(fd); return 0; } d += r; } d = 0; while(d != (ssize_t)len) { if((r=write(fd, buf+d, len-d)) == -1) { if(errno == EAGAIN) continue; /* temporarily unavail: try again*/ log_err("tube msg write failed: %s", strerror(errno)); (void)fd_set_nonblock(fd); return 0; } d += r; } if(!fd_set_nonblock(fd)) return 0; return 1; }
int pr_log_openfile(const char *log_file, int *log_fd, mode_t log_mode) { int res; pool *tmp_pool = NULL; char *tmp = NULL, *lf; unsigned char have_stat = FALSE, *allow_log_symlinks = NULL; struct stat st; /* Sanity check */ if (!log_file || !log_fd) { errno = EINVAL; return -1; } /* Make a temporary copy of log_file in case it's a constant */ tmp_pool = make_sub_pool(permanent_pool); pr_pool_tag(tmp_pool, "log_openfile() tmp pool"); lf = pstrdup(tmp_pool, log_file); tmp = strrchr(lf, '/'); if (tmp == NULL) { pr_log_debug(DEBUG0, "inappropriate log file: %s", lf); destroy_pool(tmp_pool); errno = EINVAL; return -1; } /* Set the path separator to zero, in order to obtain the directory * name, so that checks of the directory may be made. */ *tmp = '\0'; if (stat(lf, &st) < 0) { int xerrno = errno; pr_log_debug(DEBUG0, "error: unable to stat() %s: %s", lf, strerror(errno)); destroy_pool(tmp_pool); errno = xerrno; return -1; } /* The path must be in a valid directory */ if (!S_ISDIR(st.st_mode)) { pr_log_debug(DEBUG0, "error: %s is not a directory", lf); destroy_pool(tmp_pool); errno = ENOTDIR; return -1; } /* Do not log to world-writable directories */ if (st.st_mode & S_IWOTH) { pr_log_pri(PR_LOG_NOTICE, "error: %s is a world-writable directory", lf); destroy_pool(tmp_pool); return PR_LOG_WRITABLE_DIR; } /* Restore the path separator so that checks on the file itself may be * done. */ *tmp = '/'; allow_log_symlinks = get_param_ptr(main_server->conf, "AllowLogSymlinks", FALSE); if (allow_log_symlinks == NULL || *allow_log_symlinks == FALSE) { int flags = O_APPEND|O_CREAT|O_WRONLY; #ifdef PR_USE_NONBLOCKING_LOG_OPEN /* Use the O_NONBLOCK flag when opening log files, as they might be * FIFOs whose other end is not currently running; we do not want to * block indefinitely in such cases. */ flags |= O_NONBLOCK; #endif /* PR_USE_NONBLOCKING_LOG_OPEN */ #ifdef O_NOFOLLOW /* On systems that support the O_NOFOLLOW flag (e.g. Linux and FreeBSD), * use it so that the path being opened, if it is a symlink, is not * followed. */ flags |= O_NOFOLLOW; #elif defined(SOLARIS2) /* Solaris doesn't support the O_NOFOLLOW flag. Instead, in their * wisdom (hah!), Solaris decided that if the given path is a symlink * and the flags O_CREAT and O_EXCL are set, the link is not followed. * Right. The problem here is the case where the path is not a symlink; * using O_CREAT|O_EXCL will then cause the open() to fail if the * file already exists. */ flags |= O_EXCL; #endif /* O_NOFOLLOW or SOLARIS2 */ *log_fd = open(lf, flags, log_mode); if (*log_fd < 0) { if (errno != EEXIST) { destroy_pool(tmp_pool); /* More portability fun: Linux likes to report ELOOP if O_NOFOLLOW * is used to open a symlink file; FreeBSD likes to return EMLINK. * Both would lead to rather misleading error messages being * logged. Catch these errnos, and return the value that properly * informs the caller that the given path was an illegal symlink. */ switch (errno) { #ifdef ELOOP case ELOOP: return PR_LOG_SYMLINK; #endif /* ELOOP */ #ifdef EMLINK case EMLINK: return PR_LOG_SYMLINK; #endif /* EMLINK */ } return -1; } else { #if defined(SOLARIS2) /* On Solaris, because of the stupid multiplexing of O_CREAT and * O_EXCL to get open() not to follow a symlink, it's possible that * the path already exists. Now, we'll try to open() without * O_EXCL, then lstat() the path to see if this pre-existing file is * a symlink or a regular file. * * Note that because this check cannot be done atomically on Solaris, * the possibility of a race condition/symlink attack still exists. * Solaris doesn't provide a good way around this situation. */ flags &= ~O_EXCL; *log_fd = open(lf, flags, log_mode); if (*log_fd < 0) { destroy_pool(tmp_pool); return -1; } /* The race condition on Solaris is here, between the open() call * above and the lstat() call below... */ if (lstat(lf, &st) != -1) have_stat = TRUE; #else destroy_pool(tmp_pool); return -1; #endif /* SOLARIS2 */ } } /* Stat the file using the descriptor, not the path */ if (!have_stat && fstat(*log_fd, &st) != -1) have_stat = TRUE; if (!have_stat || S_ISLNK(st.st_mode)) { pr_log_debug(DEBUG0, !have_stat ? "error: unable to stat %s" : "error: %s is a symbolic link", lf); close(*log_fd); *log_fd = -1; destroy_pool(tmp_pool); return PR_LOG_SYMLINK; } } else { int flags = O_CREAT|O_APPEND|O_WRONLY; #ifdef PR_USE_NONBLOCKING_LOG_OPEN /* Use the O_NONBLOCK flag when opening log files, as they might be * FIFOs whose other end is not currently running; we do not want to * block indefinitely in such cases. */ flags |= O_NONBLOCK; #endif /* PR_USE_NONBLOCKING_LOG_OPEN */ *log_fd = open(lf, flags, log_mode); if (*log_fd < 0) { destroy_pool(tmp_pool); return -1; } } /* Find a usable fd for the just-opened log fd. */ if (*log_fd <= STDERR_FILENO) { res = pr_fs_get_usable_fd(*log_fd); if (res < 0) { pr_log_debug(DEBUG0, "warning: unable to find good fd for logfd %d: %s", *log_fd, strerror(errno)); } else { close(*log_fd); *log_fd = res; } } if (fcntl(*log_fd, F_SETFD, FD_CLOEXEC) < 0) { pr_log_pri(PR_LOG_WARNING, "unable to set CLO_EXEC on log fd %d: %s", *log_fd, strerror(errno)); } #ifdef PR_USE_NONBLOCKING_LOG_OPEN /* Return the fd to blocking mode. */ (void) fd_set_block(*log_fd); #endif /* PR_USE_NONBLOCKING_LOG_OPEN */ destroy_pool(tmp_pool); return 0; }
int tube_read_msg(struct tube* tube, uint8_t** buf, uint32_t* len, int nonblock) { ssize_t r, d; int fd = tube->sr; /* test */ *len = 0; if(nonblock) { r = read(fd, len, sizeof(*len)); if(r == -1) { if(errno==EINTR || errno==EAGAIN) return -1; log_err("tube msg read failed: %s", strerror(errno)); return -1; /* we can still continue, perhaps */ } if(r == 0) /* EOF */ return 0; } else r = 0; if(!fd_set_block(fd)) return 0; /* read remainder */ d = r; while(d != (ssize_t)sizeof(*len)) { if((r=read(fd, ((char*)len)+d, sizeof(*len)-d)) == -1) { log_err("tube msg read failed: %s", strerror(errno)); (void)fd_set_nonblock(fd); return 0; } if(r == 0) /* EOF */ { (void)fd_set_nonblock(fd); return 0; } d += r; } log_assert(*len < 65536*2); *buf = (uint8_t*)malloc(*len); if(!*buf) { log_err("tube read out of memory"); (void)fd_set_nonblock(fd); return 0; } d = 0; while(d < (ssize_t)*len) { if((r=read(fd, (*buf)+d, (size_t)((ssize_t)*len)-d)) == -1) { log_err("tube msg read failed: %s", strerror(errno)); (void)fd_set_nonblock(fd); free(*buf); return 0; } if(r == 0) { /* EOF */ (void)fd_set_nonblock(fd); free(*buf); return 0; } d += r; } if(!fd_set_nonblock(fd)) { free(*buf); return 0; } return 1; }