/*
- * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
char *tty;
char *cmd;
time_t tstamp;
+ int rows;
+ int cols;
};
/*
extern char *get_timestr __P((time_t, int));
extern int term_raw __P((int, int));
extern int term_restore __P((int, int));
+extern void get_ttysize __P((int *rowp, int *colp));
RETSIGTYPE cleanup __P((int));
static int list_sessions __P((int, char **, const char *, const char *, const char *));
static void usage __P((int));
static int open_io_fd __P((char *pathbuf, int len, const char *suffix, union io_fd *fdp));
static int parse_timing __P((const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes));
+static struct log_info *parse_logfile __P((char *logfile));
+static void free_log_info __P((struct log_info *li));
#ifdef HAVE_REGCOMP
# define REGEX_T regex_t
char *argv[];
{
int ch, idx, plen, nready, interactive = 0, listonly = 0;
+ int rows = 0, cols = 0;
const char *id, *user = NULL, *pattern = NULL, *tty = NULL, *decimal = ".";
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
double seconds, to_wait, speed = 1.0, max_wait = 0;
- FILE *lfile;
fd_set *fdsw;
sigaction_t sa;
size_t len, nbytes, nread, off;
ssize_t nwritten;
+ struct log_info *li;
Argc = argc;
Argv = argv;
}
}
- /* Read log file. */
+ /* Parse log file. */
path[plen] = '\0';
strlcat(path, "/log", sizeof(path));
- lfile = fopen(path, "r");
- if (lfile == NULL)
- error(1, "unable to open %s", path);
- cp = NULL;
- len = 0;
- /* Pull out command (third line). */
- if (getline(&cp, &len, lfile) == -1 ||
- getline(&cp, &len, lfile) == -1 ||
- getline(&cp, &len, lfile) == -1) {
- errorx(1, "invalid log file %s", path);
+ if ((li = parse_logfile(path)) == NULL)
+ exit(1);
+ printf("Replaying sudo session: %s", li->cmd);
+
+ /* Make sure the terminal is large enough. */
+ get_ttysize(&rows, &cols);
+ if (li->rows != 0 && li->cols != 0) {
+ if (li->rows > rows) {
+ printf("Warning: your terminal is too small to properly replay the log.\n");
+ printf("Log geometry is %d x %d, your terminal's geometry is %d x %d.", li->rows, li->cols, rows, cols);
+ }
}
- printf("Replaying sudo session: %s", cp);
- free(cp);
- fclose(lfile);
+
+ /* Done with parsed log file. */
+ free_log_info(li);
+ li = NULL;
fflush(stdout);
memset(&sa, 0, sizeof(sa));
return matched;
}
-static int
-list_session(logfile, re, user, tty)
+static struct log_info *
+parse_logfile(logfile)
char *logfile;
- REGEX_T *re;
- const char *user;
- const char *tty;
{
FILE *fp;
- char *buf = NULL, *cmd = NULL, *cwd = NULL, idbuf[7], *idstr, *cp;
- struct log_info li;
+ char *buf = NULL, *cp, *ep;
size_t bufsize = 0, cwdsize = 0, cmdsize = 0;
- int rval = -1;
+ struct log_info *li = NULL;
fp = fopen(logfile, "r");
if (fp == NULL) {
warning("unable to open %s", logfile);
- goto done;
+ goto bad;
}
/*
* 2) cwd
* 3) command with args
*/
+ li = ecalloc(1, sizeof(*li));
if (getline(&buf, &bufsize, fp) == -1 ||
- getline(&cwd, &cwdsize, fp) == -1 ||
- getline(&cmd, &cmdsize, fp) == -1) {
- goto done;
+ getline(&li->cwd, &cwdsize, fp) == -1 ||
+ getline(&li->cmd, &cmdsize, fp) == -1) {
+ goto bad;
}
- /* crack the log line: timestamp:user:runas_user:runas_group:tty */
+ /* Strip the newline from the cwd and command. */
+ li->cwd[strcspn(li->cwd, "\n")] = '\0';
+ li->cmd[strcspn(li->cmd, "\n")] = '\0';
+
+ /*
+ * Crack the log line (rows and cols not present in old versions).
+ * timestamp:user:runas_user:runas_group:tty:rows:cols
+ */
buf[strcspn(buf, "\n")] = '\0';
- if ((li.tstamp = atoi(buf)) == 0)
- goto done;
- if ((cp = strchr(buf, ':')) == NULL)
- goto done;
- *cp++ = '\0';
- li.user = cp;
+ /* timestamp */
+ if ((ep = strchr(buf, ':')) == NULL)
+ goto bad;
+ if ((li->tstamp = atoi(buf)) == 0)
+ goto bad;
- if ((cp = strchr(cp, ':')) == NULL)
- goto done;
- *cp++ = '\0';
- li.runas_user = cp;
+ /* user */
+ cp = ep + 1;
+ if ((ep = strchr(cp, ':')) == NULL)
+ goto bad;
+ li->user = estrndup(cp, (size_t)(ep - cp));
- if ((cp = strchr(cp, ':')) == NULL)
- goto done;
- *cp++ = '\0';
- li.runas_group = cp;
+ /* runas user */
+ cp = ep + 1;
+ if ((ep = strchr(cp, ':')) == NULL)
+ goto bad;
+ li->runas_user = estrndup(cp, (size_t)(ep - cp));
- if ((cp = strchr(cp, ':')) == NULL)
- goto done;
- *cp++ = '\0';
- li.tty = cp;
+ /* runas group */
+ cp = ep + 1;
+ if ((ep = strchr(cp, ':')) == NULL)
+ goto bad;
+ if (cp != ep)
+ li->runas_group = estrndup(cp, (size_t)(ep - cp));
- cwd[strcspn(cwd, "\n")] = '\0';
- li.cwd = cwd;
+ /* tty, followed by optional rows + columns */
+ cp = ep + 1;
+ if ((ep = strchr(cp, ':')) == NULL) {
+ li->tty = estrdup(cp);
+ } else {
+ li->tty = estrndup(cp, (size_t)(ep - cp));
+ cp = ep + 1;
+ li->rows = atoi(cp);
+ if ((ep = strchr(cp, ':')) != NULL) {
+ cp = ep + 1;
+ li->cols = atoi(cp);
+ }
+ }
+ fclose(fp);
+ efree(buf);
+ return li;
- cmd[strcspn(cmd, "\n")] = '\0';
- li.cmd = cmd;
+bad:
+ fclose(fp);
+ efree(buf);
+ free_log_info(li);
+ return NULL;
+}
+
+static void
+free_log_info(li)
+ struct log_info *li;
+{
+ if (li != NULL) {
+ efree(li->cwd);
+ efree(li->user);
+ efree(li->runas_user);
+ efree(li->runas_group);
+ efree(li->tty);
+ efree(li->cmd);
+ efree(li);
+ }
+}
+
+static int
+list_session(logfile, re, user, tty)
+ char *logfile;
+ REGEX_T *re;
+ const char *user;
+ const char *tty;
+{
+ char idbuf[7], *idstr, *cp;
+ struct log_info *li;
+ int rval = -1;
+
+ if ((li = parse_logfile(logfile)) == NULL)
+ goto done;
/* Match on search expression if there is one. */
- if (search_expr && !match_expr(search_expr, &li))
+ if (search_expr && !match_expr(search_expr, li))
goto done;
/* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */
cp[strlen(cp) - 4] = '\0';
idstr = cp;
}
+ /* XXX - print rows + cols? */
printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ",
- get_timestr(li.tstamp, 1), li.user, li.tty, li.cwd, li.runas_user);
- if (*li.runas_group)
- printf("GROUP=%s ; ", li.runas_group);
- printf("TSID=%s ; COMMAND=%s\n", idstr, li.cmd);
+ get_timestr(li->tstamp, 1), li->user, li->tty, li->cwd, li->runas_user);
+ if (li->runas_group)
+ printf("GROUP=%s ; ", li->runas_group);
+ printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd);
rval = 0;
done:
- fclose(fp);
+ free_log_info(li);
return rval;
}
--- /dev/null
+/*
+ * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <termios.h>
+
+#include "missing.h"
+
+/* Compatibility with older tty systems. */
+#if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
+# define TIOCGWINSZ TIOCGSIZE
+# define winsize ttysize
+# define ws_col ts_cols
+# define ws_row ts_lines
+#endif
+
+#ifdef TIOCGWINSZ
+static int
+get_ttysize_ioctl(int *rowp, int *colp)
+{
+ struct winsize wsize;
+
+ if (ioctl(STDERR_FILENO, TIOCGWINSZ, &wsize) == 0 &&
+ wsize.ws_row != 0 && wsize.ws_col != 0) {
+ *rowp = wsize.ws_row;
+ *colp = wsize.ws_col;
+ return 0;
+ }
+ return -1;
+}
+#else
+static int
+get_ttysize_ioctl(int *rowp, int *colp)
+{
+ return -1;
+}
+#endif /* TIOCGWINSZ */
+
+void
+get_ttysize(int *rowp, int *colp)
+{
+ if (get_ttysize_ioctl(rowp, colp) == -1) {
+ char *p;
+
+ /* Fall back on $LINES and $COLUMNS. */
+ if ((p = getenv("LINES")) == NULL || (*rowp = atoi(p)) <= 0)
+ *rowp = 24;
+ if ((p = getenv("COLUMNS")) == NULL || (*colp = atoi(p)) <= 0)
+ *colp = 80;
+ }
+
+ return;
+}