diff options
-rw-r--r-- | pseudo.c | 14 | ||||
-rw-r--r-- | pseudo.h | 4 | ||||
-rw-r--r-- | pseudo_client.c | 19 | ||||
-rw-r--r-- | pseudo_db.c | 110 | ||||
-rw-r--r-- | pseudo_db.h | 6 | ||||
-rw-r--r-- | pseudo_ipc.h | 4 | ||||
-rw-r--r-- | pseudo_server.c | 187 | ||||
-rw-r--r-- | pseudo_server.h | 2 | ||||
-rw-r--r-- | pseudo_table.c | 35 | ||||
-rw-r--r-- | pseudolog.1 | 40 | ||||
-rw-r--r-- | pseudolog.c | 49 |
11 files changed, 322 insertions, 148 deletions
@@ -45,7 +45,7 @@ long opt_p = 0; char *opt_r = NULL; int opt_S = 0; -static int pseudo_op(pseudo_msg_t *msg, const char *tag); +static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag); void usage(int status) { @@ -273,7 +273,7 @@ main(int argc, char *argv[]) { * sanity checks, then implements the fairly small DB changes required. */ int -pseudo_op(pseudo_msg_t *msg, const char *tag) { +pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) { pseudo_msg_t msg_header; pseudo_msg_t by_path = { .op = 0 }, by_ino = { .op = 0 }; pseudo_msg_t db_header; @@ -678,27 +678,27 @@ pseudo_op(pseudo_msg_t *msg, const char *tag) { free(path_by_ino); pseudo_debug(2, "completed %s.\n", pseudo_op_name(msg->op)); if (opt_l) - pdb_log_msg(SEVERITY_INFO, msg, tag, NULL); + pdb_log_msg(SEVERITY_INFO, msg, program, tag, NULL); return 0; } /* SHUTDOWN does not get this far, it's handled in pseudo_server.c */ int -pseudo_server_response(pseudo_msg_t *msg, const char *tag) { +pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag) { switch (msg->type) { case PSEUDO_MSG_PING: msg->result = RESULT_SUCCEED; if (opt_l) - pdb_log_msg(SEVERITY_INFO, msg, tag, "ping"); + pdb_log_msg(SEVERITY_INFO, msg, program, tag, NULL); return 0; break; case PSEUDO_MSG_OP: - return pseudo_op(msg, tag); + return pseudo_op(msg, program, tag); break; case PSEUDO_MSG_ACK: /* FALLTHROUGH */ case PSEUDO_MSG_NAK: /* FALLTHROUGH */ default: - pdb_log_msg(SEVERITY_WARN, msg, tag, "invalid message"); + pdb_log_msg(SEVERITY_WARN, msg, program, tag, "invalid message"); return 1; } } @@ -99,8 +99,8 @@ typedef enum pseudo_query_field { PSQF_CLIENT, PSQF_DEV, PSQF_FD, PSQF_FTYPE, PSQF_GID, PSQF_ID, PSQF_INODE, PSQF_MODE, PSQF_OP, PSQF_ORDER, PSQF_PATH, PSQF_PERM, - PSQF_RESULT, PSQF_SEVERITY, PSQF_STAMP, PSQF_TAG, - PSQF_TEXT, PSQF_UID, + PSQF_PROGRAM, PSQF_RESULT, PSQF_SEVERITY, PSQF_STAMP, + PSQF_TAG, PSQF_TEXT, PSQF_TYPE, PSQF_UID, PSQF_MAX } pseudo_query_field_t; extern char *pseudo_query_field_name(pseudo_query_field_t id); diff --git a/pseudo_client.c b/pseudo_client.c index a8550ef..67510db 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -37,6 +37,9 @@ #include "pseudo_ipc.h" #include "pseudo_client.h" +/* GNU extension */ +extern char *program_invocation_name; + static char *base_path(int dirfd, const char *path, int leave_last); static int connect_fd = -1; @@ -465,20 +468,24 @@ static int client_ping(void) { pseudo_msg_t ping; pseudo_msg_t *ack; + char tagbuf[pseudo_path_max()]; char *tag = getenv("PSEUDO_TAG"); ping.type = PSEUDO_MSG_PING; ping.op = OP_NONE; - if (tag) { - ping.pathlen = strlen(tag); - } else { - ping.pathlen = 0; - } + + if (!tag) + tag = ""; + + ping.pathlen = snprintf(tagbuf, sizeof(tagbuf), "%s%c%s", + program_invocation_name ? program_invocation_name : "<unknown>", + 0, + tag); ping.client = getpid(); ping.result = 0; errno = 0; pseudo_debug(4, "sending ping\n"); - if (pseudo_msg_send(connect_fd, &ping, ping.pathlen, tag)) { + if (pseudo_msg_send(connect_fd, &ping, ping.pathlen, tagbuf)) { pseudo_debug(3, "error pinging server: %s\n", strerror(errno)); return 1; } diff --git a/pseudo_db.c b/pseudo_db.c index 88fa96a..c256b4a 100644 --- a/pseudo_db.c +++ b/pseudo_db.c @@ -223,6 +223,10 @@ static struct sql_migration { { "ALTER TABLE logs ADD gid INTEGER;" }, /* track access types (read/write, etc) */ { "ALTER TABLE logs ADD access INTEGER;" }, + /* client program/path */ + { "ALTER TABLE logs ADD program VARCHAR;" }, + /* message type (ping, op) */ + { "ALTER TABLE logs ADD type INTEGER;" }, { NULL }, }; @@ -595,6 +599,10 @@ pdb_log_traits(pseudo_query_t *traits) { case PSQF_PERM: e->mode |= (trait->data.ivalue & ~(S_IFMT) & 0177777); break; + case PSQF_PROGRAM: + e->program = trait->data.svalue ? + strdup(trait->data.svalue) : NULL; + break; case PSQF_RESULT: e->result = trait->data.ivalue; break; @@ -612,6 +620,9 @@ pdb_log_traits(pseudo_query_t *traits) { e->text = trait->data.svalue ? strdup(trait->data.svalue) : NULL; break; + case PSQF_TYPE: + e->type = trait->data.ivalue; + break; case PSQF_UID: e->uid = trait->data.ivalue; break; @@ -634,9 +645,9 @@ pdb_log_traits(pseudo_query_t *traits) { int pdb_log_entry(log_entry *e) { char *sql = "INSERT INTO logs " - "(stamp, op, access, client, dev, gid, ino, mode, path, result, severity, text, tag, uid)" + "(stamp, op, access, client, dev, gid, ino, mode, path, result, severity, text, program, tag, type, uid)" " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; static sqlite3_stmt *insert; int field; int rc; @@ -680,11 +691,17 @@ pdb_log_entry(log_entry *e) { } else { sqlite3_bind_null(insert, field++); } + if (e->program) { + sqlite3_bind_text(insert, field++, e->program, -1, SQLITE_STATIC); + } else { + sqlite3_bind_null(insert, field++); + } if (e->tag) { sqlite3_bind_text(insert, field++, e->tag, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } + sqlite3_bind_int(insert, field++, e->type); sqlite3_bind_int(insert, field++, e->uid); } else { sqlite3_bind_int(insert, field++, (unsigned long) time(NULL)); @@ -700,6 +717,8 @@ pdb_log_entry(log_entry *e) { sqlite3_bind_int(insert, field++, 0); sqlite3_bind_null(insert, field++); sqlite3_bind_null(insert, field++); + sqlite3_bind_null(insert, field++); + sqlite3_bind_int(insert, field++, 0); sqlite3_bind_int(insert, field++, 0); } @@ -713,11 +732,11 @@ pdb_log_entry(log_entry *e) { } /* create a log from a given message, with tag and text */ int -pdb_log_msg(sev_id_t severity, pseudo_msg_t *msg, const char *tag, const char *text, ...) { +pdb_log_msg(sev_id_t severity, pseudo_msg_t *msg, const char *program, const char *tag, const char *text, ...) { char *sql = "INSERT INTO logs " - "(stamp, op, access, client, dev, gid, ino, mode, path, result, uid, severity, text, tag)" + "(stamp, op, access, client, dev, gid, ino, mode, path, result, uid, severity, text, program, tag, type)" " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; static sqlite3_stmt *insert; char buffer[8192]; int field; @@ -779,11 +798,21 @@ pdb_log_msg(sev_id_t severity, pseudo_msg_t *msg, const char *tag, const char *t } else { sqlite3_bind_null(insert, field++); } + if (program) { + sqlite3_bind_text(insert, field++, program, -1, SQLITE_STATIC); + } else { + sqlite3_bind_null(insert, field++); + } if (tag) { sqlite3_bind_text(insert, field++, tag, -1, SQLITE_STATIC); } else { sqlite3_bind_null(insert, field++); } + if (msg) { + sqlite3_bind_int(insert, field++, msg->type); + } else { + sqlite3_bind_int(insert, field++, 0); + } rc = sqlite3_step(insert); if (rc != SQLITE_DONE) { @@ -844,7 +873,7 @@ frag(buffer *b, char *fmt, ...) { } log_history -pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { +pdb_history(pseudo_query_t *traits, unsigned long fields, int unique, int do_delete) { log_history h = NULL; pseudo_query_t *trait; sqlite3_stmt *select; @@ -856,7 +885,6 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { pseudo_query_field_t f; static buffer *sql; - /* this column arrangement is used by pdb_history_entry() */ if (!sql) { sql = malloc(sizeof *sql); if (!sql) { @@ -872,23 +900,28 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { } } sql->tail = sql->data; - frag(sql, "SELECT "); + if (do_delete) + frag(sql, "DELETE "); + else + frag(sql, "SELECT "); if (!log_db && get_db(&log_db)) { pseudo_diag("database error.\n"); return 0; } - if (distinct) - frag(sql, "DISTINCT "); + if (!do_delete) { + if (unique) + frag(sql, "DISTINCT "); - done_any = 0; - for (f = PSQF_NONE + 1; f < PSQF_MAX; ++f) { - if (fields & (1 << f)) { - frag(sql, "%s%s", - done_any ? ", " : "", - pseudo_query_field_name(f)); - done_any = 1; + done_any = 0; + for (f = PSQF_NONE + 1; f < PSQF_MAX; ++f) { + if (fields & (1 << f)) { + frag(sql, "%s%s", + done_any ? ", " : "", + pseudo_query_field_name(f)); + done_any = 1; + } } } @@ -905,6 +938,7 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { } } switch (trait->field) { + case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ case PSQF_TAG: /* FALLTHROUGH */ case PSQF_PATH: @@ -996,13 +1030,14 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { break; } } - frag(sql, "ORDER BY %s %s;", order_by, order_dir); + if (!do_delete) + frag(sql, "ORDER BY %s %s;", order_by, order_dir); pseudo_debug(1, "created SQL: <%s>\n", sql->data); /* second, prepare it */ rc = sqlite3_prepare_v2(log_db, sql->data, strlen(sql->data), &select, NULL); if (rc) { - dberr(log_db, "couldn't prepare SELECT statement"); + dberr(log_db, "couldn't prepare %s statement", do_delete ? "DELETE" : "SELECT"); return 0; } @@ -1013,6 +1048,7 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { case PSQF_ORDER: /* this just creates a hunk of SQL above */ break; + case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_PATH: /* FALLTHROUGH */ case PSQF_TAG: /* FALLTHROUGH */ case PSQF_TEXT: @@ -1032,6 +1068,7 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { case PSQF_RESULT: /* FALLTHROUGH */ case PSQF_SEVERITY: /* FALLTHROUGH */ case PSQF_STAMP: /* FALLTHROUGH */ + case PSQF_TYPE: /* FALLTHROUGH */ case PSQF_UID: /* FALLTHROUGH */ sqlite3_bind_int(select, field++, trait->data.ivalue); break; @@ -1042,6 +1079,16 @@ pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct) { } } + if (do_delete) { + /* no need to return it, so... */ + int rc = sqlite3_step(select); + if (rc != SQLITE_DONE) { + dberr(log_db, "deletion failed"); + } + sqlite3_finalize(select); + return 0; + } + /* fourth, return the statement, now ready to be stepped through */ h = malloc(sizeof(*h)); if (h) { @@ -1114,6 +1161,11 @@ pdb_history_entry(log_history h) { if (s) l->path = strdup((char *) s); break; + case PSQF_PROGRAM: + s = sqlite3_column_text(h->stmt, column++); + if (s) + l->program = strdup((char *) s); + break; case PSQF_RESULT: l->result = sqlite3_column_int64(h->stmt, column++); break; @@ -1133,6 +1185,9 @@ pdb_history_entry(log_history h) { if (s) l->text = strdup((char *) s); break; + case PSQF_TYPE: + l->type = sqlite3_column_int64(h->stmt, column++); + break; case PSQF_UID: l->uid = sqlite3_column_int64(h->stmt, column++); break; @@ -1170,6 +1225,7 @@ log_entry_free(log_entry *e) { return; free(e->text); free(e->path); + free(e->program); free(e->tag); free(e); } @@ -1228,7 +1284,7 @@ pdb_link_file(pseudo_msg_t *msg) { /* pdb_unlink_file_dev: Delete every instance of a dev/inode pair. */ int pdb_unlink_file_dev(pseudo_msg_t *msg) { - static sqlite3_stmt *delete; + static sqlite3_stmt *sql_delete; int rc; char *sql = "DELETE FROM files WHERE dev = ? AND ino = ?;"; @@ -1236,8 +1292,8 @@ pdb_unlink_file_dev(pseudo_msg_t *msg) { pseudo_diag("database error.\n"); return 0; } - if (!delete) { - rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &delete, NULL); + if (!sql_delete) { + rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &sql_delete, NULL); if (rc) { dberr(file_db, "couldn't prepare DELETE statement"); return 1; @@ -1246,14 +1302,14 @@ pdb_unlink_file_dev(pseudo_msg_t *msg) { if (!msg) { return 1; } - sqlite3_bind_int(delete, 1, msg->dev); - sqlite3_bind_int(delete, 2, msg->ino); - rc = sqlite3_step(delete); + sqlite3_bind_int(sql_delete, 1, msg->dev); + sqlite3_bind_int(sql_delete, 2, msg->ino); + rc = sqlite3_step(sql_delete); if (rc != SQLITE_DONE) { dberr(file_db, "delete by inode may have failed"); } - sqlite3_reset(delete); - sqlite3_clear_bindings(delete); + sqlite3_reset(sql_delete); + sqlite3_clear_bindings(sql_delete); return rc != SQLITE_DONE; } diff --git a/pseudo_db.h b/pseudo_db.h index 9b82c53..aeacc95 100644 --- a/pseudo_db.h +++ b/pseudo_db.h @@ -19,6 +19,7 @@ */ typedef struct { time_t stamp; + pseudo_msg_type_t type; op_id_t op; int access; unsigned long client; @@ -33,6 +34,7 @@ typedef struct { sev_id_t severity; char *text; char *tag; + char *program; } log_entry; extern int pdb_link_file(pseudo_msg_t *msg); @@ -63,10 +65,10 @@ typedef struct pseudo_query { } pseudo_query_t; extern int pdb_log_entry(log_entry *e); -extern int pdb_log_msg(sev_id_t severity, pseudo_msg_t *msg, const char *tag, const char *text, ...); +extern int pdb_log_msg(sev_id_t severity, pseudo_msg_t *msg, const char *program, const char *tag, const char *text, ...); extern int pdb_log_traits(pseudo_query_t *traits); -extern log_history pdb_history(pseudo_query_t *traits, unsigned long fields, int distinct); +extern log_history pdb_history(pseudo_query_t *traits, unsigned long fields, int unique, int delete); extern log_entry *pdb_history_entry(log_history h); extern void pdb_history_free(log_history h); extern void log_entry_free(log_entry *); diff --git a/pseudo_ipc.h b/pseudo_ipc.h index 063a1ef..664cbd2 100644 --- a/pseudo_ipc.h +++ b/pseudo_ipc.h @@ -18,6 +18,8 @@ * */ typedef enum { + PSEUDO_MSG_UNKNOWN = -1, + PSEUDO_MSG_NONE, PSEUDO_MSG_PING, PSEUDO_MSG_SHUTDOWN, PSEUDO_MSG_OP, @@ -25,6 +27,8 @@ typedef enum { PSEUDO_MSG_NAK, PSEUDO_MSG_MAX } pseudo_msg_type_t; +extern char *pseudo_msg_type_name(pseudo_msg_type_t id); +extern pseudo_msg_type_t pseudo_msg_type_id(char *name); /* The [] item at the end of the struct is a C99 feature, replacing the * old (and unportable) "struct hack". diff --git a/pseudo_server.c b/pseudo_server.c index 4a474d0..931f77c 100644 --- a/pseudo_server.c +++ b/pseudo_server.c @@ -42,9 +42,15 @@ static int listen_fd = -1; -int *client_fds; -pid_t *client_pids; -char **client_tags; +typedef struct { + int fd; + pid_t pid; + char *tag; + char *program; +} pseudo_client_t; + +pseudo_client_t *clients; + /* active_clients: Number of clients we actually have right now. * highest_client: Highest index into clients table of an active client. * max_clients: Size of table. @@ -178,18 +184,17 @@ pseudo_server_start(int daemonize) { /* mess with internal tables as needed */ static void open_client(int fd) { - int *new_client_fds; - pid_t *new_client_pids; - char **new_client_tags; + pseudo_client_t *new_clients; int i; /* if possible, use first open client slot */ for (i = 0; i < max_clients; ++i) { - if (client_fds[i] == -1) { + if (clients[i].fd == -1) { pseudo_debug(2, "reusing client %d for fd %d\n", i, fd); - client_fds[i] = fd; - client_pids[i] = 0; - client_tags[i] = 0; + clients[i].fd = fd; + clients[i].pid = 0; + clients[i].tag = NULL; + clients[i].program = NULL; ++active_clients; if (i > highest_client) highest_client = i; @@ -198,37 +203,26 @@ open_client(int fd) { } /* otherwise, allocate a new one */ - new_client_fds = malloc(sizeof(int) * (max_clients + 16)); - new_client_pids = malloc(sizeof(pid_t) * (max_clients + 16)); - new_client_tags = malloc(sizeof(char *) * (max_clients + 16)); - if (new_client_fds && new_client_pids && new_client_tags) { - memcpy(new_client_fds, client_fds, max_clients * sizeof(int)); - memcpy(new_client_pids, client_pids, max_clients * sizeof(pid_t)); - memcpy(new_client_tags, client_tags, max_clients * sizeof(char *)); - free(client_fds); - free(client_pids); - free(client_tags); + new_clients = malloc(sizeof(*new_clients) * (max_clients + 16)); + if (new_clients) { + memcpy(new_clients, clients, max_clients * sizeof(*clients)); + free(clients); for (i = max_clients; i < max_clients + 16; ++i) { - new_client_fds[i] = -1; - new_client_pids[i] = 0; - new_client_tags[i] = 0; + new_clients[i].fd = -1; + new_clients[i].pid = 0; + new_clients[i].tag = NULL; + new_clients[i].program = NULL; } - client_fds = new_client_fds; - client_pids = new_client_pids; - client_tags = new_client_tags; - - client_fds[max_clients] = fd; - client_pids[max_clients] = 0; - client_tags[max_clients] = 0; + clients = new_clients; + clients[max_clients].fd = fd; + clients[max_clients].pid = 0; + clients[max_clients].tag = NULL; + clients[max_clients].program = NULL; highest_client = max_clients + 1; max_clients += 16; ++active_clients; } else { - /* if something got allocated, free it */ - free(new_client_fds); - free(new_client_pids); - free(new_client_tags); pseudo_diag("error allocating new client, fd %d\n", fd); close(fd); } @@ -239,16 +233,24 @@ open_client(int fd) { */ static void close_client(int client) { - pseudo_debug(2, "lost client %d [%d], closing fd %d\n", client, client_pids[client], client_fds[client]); + pseudo_debug(2, "lost client %d [%d], closing fd %d\n", client, + clients[client].pid, clients[client].fd); /* client went away... */ - close(client_fds[client]); - client_fds[client] = -1; - free(client_tags[client]); - client_tags[client] = 0; - client_pids[client] = 0; + if (client > highest_client || client <= 0) { + pseudo_diag("tried to close client %d (highest is %d)\n", + client, highest_client); + return; + } + close(clients[client].fd); + clients[client].fd = -1; + free(clients[client].tag); + free(clients[client].program); + clients[client].pid = 0; + clients[client].tag = NULL; + clients[client].program = NULL; --active_clients; if (client == highest_client) - while (client_fds[highest_client] != -1 && highest_client > 0) + while (clients[highest_client].fd != -1 && highest_client > 0) --highest_client; } @@ -259,34 +261,50 @@ serve_client(int i) { pseudo_msg_t *in; int rc; - pseudo_debug(2, "message from client %d [%d:%s] fd %d\n", - i, (int) client_pids[i], - client_tags[i] ? client_tags[i] : "NO TAG", - client_fds[i]); - in = pseudo_msg_receive(client_fds[i]); + pseudo_debug(2, "message from client %d [%d:%s - %s] fd %d\n", + i, (int) clients[i].pid, + clients[i].program ? clients[i].program : "???", + clients[i].tag ? clients[i].tag : "NO TAG", + clients[i].fd); + in = pseudo_msg_receive(clients[i].fd); if (in) { char *response_path = 0; pseudo_debug(4, "got a message (%d): %s\n", in->type, (in->pathlen ? in->path : "<no path>")); /* handle incoming ping */ - if (in->type == PSEUDO_MSG_PING && !client_pids[i]) { - pseudo_debug(2, "new client: %d -> %d\n", + if (in->type == PSEUDO_MSG_PING && !clients[i].pid) { + pseudo_debug(2, "new client: %d -> %d", i, in->client); - client_pids[i] = in->client; + clients[i].pid = in->client; if (in->pathlen) { - free(client_tags[i]); - client_tags[i] = strdup(in->path); + size_t proglen; + proglen = strlen(in->path); + + pseudo_debug(2, " <%s>", in->path); + free(clients[i].program); + clients[i].program = malloc(proglen + 1); + if (clients[i].program) { + snprintf(clients[i].program, proglen + 1, "%s", in->path); + } + if (in->pathlen > proglen) { + pseudo_debug(2, " [%s]", in->path + proglen + 1); + clients[i].tag = malloc(in->pathlen - proglen); + if (clients[i].tag) + snprintf(clients[i].tag, in->pathlen - proglen, + "%s", in->path + proglen + 1); + } } + pseudo_debug(2, "\n"); } /* sanity-check client ID */ - if (in->client != client_pids[i]) { + if (in->client != clients[i].pid) { pseudo_debug(1, "uh-oh, expected pid %d for client %d, got %d\n", - (int) client_pids[i], i, in->client); + (int) clients[i].pid, i, in->client); } /* regular requests are processed in place by * pseudo_server_response. */ if (in->type != PSEUDO_MSG_SHUTDOWN) { - if (pseudo_server_response(in, client_tags[i])) { + if (pseudo_server_response(in, clients[i].program, clients[i].tag)) { in->type = PSEUDO_MSG_NAK; } else { in->type = PSEUDO_MSG_ACK; @@ -312,8 +330,8 @@ serve_client(int i) { in->fd = active_clients - 2; s = response_path; for (j = 1; j <= highest_client; ++j) { - if (client_fds[j] != -1 && j != i) { - s += snprintf(s, 8, "%d ", (int) client_pids[j]); + if (clients[j].fd != -1 && j != i) { + s += snprintf(s, 8, "%d ", (int) clients[j].pid); } } in->pathlen = (s - response_path) + 1; @@ -328,7 +346,7 @@ serve_client(int i) { } if ((rc = pseudo_msg_send(clients[i].fd, in, -1, response_path)) != 0) pseudo_debug(1, "failed to send response to client %d [%d]: %d (%s)\n", - i, (int) client_pids[i], rc, strerror(errno)); + i, (int) clients[i].pid, rc, strerror(errno)); rc = in->op; free(response_path); return rc; @@ -336,7 +354,7 @@ serve_client(int i) { /* this should not be happening, but the exceptions aren't * being detected in select() for some reason. */ - pseudo_debug(2, "client %d: no message\n", (int) client_pids[i]); + pseudo_debug(2, "client %d: no message\n", (int) clients[i].pid); close_client(i); return 0; } @@ -358,17 +376,16 @@ pseudo_server_loop(void) { int fd; int loop_timeout = pseudo_server_timeout; - client_fds = malloc(sizeof(int) * 16); - client_pids = malloc(sizeof(pid_t) * 16); - client_tags = malloc(sizeof(char *) * 16); + clients = malloc(16 * sizeof(*clients)); - client_fds[0] = listen_fd; - client_pids[0] = getpid(); + clients[0].fd = listen_fd; + clients[0].pid = getpid(); for (i = 1; i < 16; ++i) { - client_fds[i] = -1; - client_pids[i] = 0; - client_tags[i] = 0; + clients[i].fd = -1; + clients[i].pid = 0; + clients[i].tag = NULL; + clients[i].program = NULL; } active_clients = 1; @@ -380,14 +397,14 @@ pseudo_server_loop(void) { pseudo_diag("got into loop with no valid listen fd.\n"); exit(1); } - pdb_log_msg(SEVERITY_INFO, NULL, NULL, "server started (pid %d)", getpid()); + pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server started (pid %d)", getpid()); FD_ZERO(&reads); FD_ZERO(&writes); FD_ZERO(&events); - FD_SET(client_fds[0], &reads); - FD_SET(client_fds[0], &events); - max_fd = client_fds[0]; + FD_SET(clients[0].fd, &reads); + FD_SET(clients[0].fd, &events); + max_fd = clients[0].fd; timeout = (struct timeval) { .tv_sec = LOOP_DELAY, .tv_usec = 0 }; /* EINTR tends to come from profiling, so it is not a good reason to @@ -414,12 +431,12 @@ pseudo_server_loop(void) { } else if (rc > 0) { loop_timeout = pseudo_server_timeout; for (i = 1; i <= highest_client; ++i) { - if (client_fds[i] == -1) + if (clients[i].fd == -1) continue; - if (FD_ISSET(client_fds[i], &events)) { + if (FD_ISSET(clients[i].fd, &events)) { /* this should happen but doesn't... */ close_client(i); - } else if (FD_ISSET(client_fds[i], &reads)) { + } else if (FD_ISSET(clients[i].fd, &reads)) { struct timeval tv1, tv2; int op; gettimeofday(&tv1, NULL); @@ -440,8 +457,8 @@ pseudo_server_loop(void) { break; } if (!(die_peacefully || die_forcefully) && - (FD_ISSET(client_fds[0], &events) || - FD_ISSET(client_fds[0], &reads))) { + (FD_ISSET(clients[0].fd, &events) || + FD_ISSET(clients[0].fd, &reads))) { len = sizeof(client); if ((fd = accept(listen_fd, (struct sockaddr *) &client, &len)) != -1) { pseudo_debug(2, "new client fd %d\n", fd); @@ -456,31 +473,31 @@ pseudo_server_loop(void) { getpid(), messages, (double) message_time.tv_sec + (double) message_time.tv_usec / 1000000.0); - pdb_log_msg(SEVERITY_INFO, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds", + pdb_log_msg(SEVERITY_INFO, NULL, NULL, NULL, "server %d exiting: handled %d messages in %.4f seconds", getpid(), messages, (double) message_time.tv_sec + (double) message_time.tv_usec / 1000000.0); - close(client_fds[0]); + close(clients[0].fd); exit(0); } FD_ZERO(&reads); FD_ZERO(&writes); FD_ZERO(&events); - FD_SET(client_fds[0], &reads); - FD_SET(client_fds[0], &events); - max_fd = client_fds[0]; + FD_SET(clients[0].fd, &reads); + FD_SET(clients[0].fd, &events); + max_fd = clients[0].fd; /* current_clients is a sanity check; note that for * purposes of select(), the server is one of the fds, * and thus, "a client". */ current_clients = 1; for (i = 1; i <= highest_client; ++i) { - if (client_fds[i] != -1) { + if (clients[i].fd != -1) { ++current_clients; - FD_SET(client_fds[i], &reads); - FD_SET(client_fds[i], &events); - if (client_fds[i] > max_fd) - max_fd = client_fds[i]; + FD_SET(clients[i].fd, &reads); + FD_SET(clients[i].fd, &events); + if (clients[i].fd > max_fd) + max_fd = clients[i].fd; } } if (current_clients != active_clients) { diff --git a/pseudo_server.h b/pseudo_server.h index 5e2ea6b..5c595ea 100644 --- a/pseudo_server.h +++ b/pseudo_server.h @@ -18,6 +18,6 @@ * */ extern int pseudo_server_start(int); -extern int pseudo_server_response(pseudo_msg_t *msg, const char *tag); +extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag); extern int pseudo_server_timeout; extern int opt_l; diff --git a/pseudo_table.c b/pseudo_table.c index 7225422..4c49f3d 100644 --- a/pseudo_table.c +++ b/pseudo_table.c @@ -17,9 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include "pseudo.h" #include <stdlib.h> #include <string.h> +#include <sys/stat.h> + +#include "pseudo.h" +#include "pseudo_ipc.h" /* just a bunch of handy lookups for pretty-printing stuff */ @@ -106,15 +109,27 @@ static char *query_field_names[] = { "order", "path", "perm", + "program", "result", "severity", "stamp", "tag", "text", + "type", "uid", NULL }; +static char *pseudo_msg_type_names[] = { + "none", + "ping", + "halt", + "op", + "ack", + "nak", + NULL +}; + char * pseudo_op_name(op_id_t id) { if (id >= OP_NONE && id < OP_MAX) { @@ -163,6 +178,14 @@ pseudo_query_field_name(pseudo_query_field_t id) { return "unknown"; } +char * +pseudo_msg_type_name(pseudo_msg_type_t id) { + if (id >= PSEUDO_MSG_NONE && id < PSEUDO_MSG_MAX) { + return pseudo_msg_type_names[id]; + } + return "????"; +} + op_id_t pseudo_op_id(char *name) { int id; @@ -212,3 +235,13 @@ pseudo_query_field(char *name) { } return PSQF_UNKNOWN; } + +pseudo_msg_type_t +pseudo_msg_type_id(char *name) { + int id; + for (id = PSEUDO_MSG_NONE; id < PSEUDO_MSG_MAX; ++id) { + if (!strcmp(name, pseudo_msg_type_names[id])) + return id; + } + return PSEUDO_MSG_NONE;; +} diff --git a/pseudolog.1 b/pseudolog.1 index b7c9bbb..f11751f 100644 --- a/pseudolog.1 +++ b/pseudolog.1 @@ -26,7 +26,7 @@ .RI [ SPECIFICATIONS ] .PP .B pseudolog -.RB [ \-DPv ] +.RB [ \-UPv ] [ .B \-E .I timeformat @@ -37,13 +37,21 @@ ] .PP .B pseudolog \-h +.PP +.B pseudolog \-D +.RB [ \-Pv ] +[ +.B \-E +.I timeformat +] +.RI [ SPECIFICATIONS ] .RI [ SPECIFICATIONS ] .SH DESCRIPTION The .I pseudolog -utility displays log entries created by the +utility displays, creates, or deletes log entries associated with the .I pseudo -daemon, or creates log entries. Creation of log entries is useful only to +daemon. Creation of log entries is useful only to create timestamps or notes; for instance, you could create a log entry before beginning a process, so there would be a timestamp for the beginning of that process. There are a number of special options used to match or create @@ -60,11 +68,7 @@ The following other options are supported: Print a usage message and exit. .TP 8 .B \-D -Restrict query output to distinct rows. Rows will have members defined by -the -.B \-F -(format) option. If all members are the same between two rows, only one -is displayed. Applies only to queries. +Delete rows selected by the query. This is not reversible. .TP 8 .BI \-E \ timeformat Specify a format string (for @@ -97,6 +101,13 @@ should be used as the .B PSEUDO_PREFIX value, overriding any environment setting. .TP 8 +.B \-U +Restrict query output to unique rows. Rows will have members defined by +the +.B \-F +(format) option. If all members are the same between two rows, only one +is displayed. Applies only to queries. +.TP 8 .B \-v Increase verbosity (debug level). Not useful except when debugging pseudo. @@ -261,6 +272,12 @@ usually means that there was no entry in the .I pseudo database. .TP 8 +.B R +Program. This is the program name (as retrieved by glibc's +.I program_invocation_name +variable), which has the full path if and only if the program +was invoked by full path name. +.TP 8 .B s Timestamp. The format of this field is controlled by the .B \-E @@ -291,6 +308,13 @@ logged. It is, of course, a text field. .TP 8 .B u UID. The user ID associated with an entry. +.TP 8 +.B y +Type. This is usually "op" for operations, or "ping" for the ping +messages clients send to confirm server availability. Other types +should rarely occur, but include "ack" and "nak" for server +responses (which are never logged), and "halt" for shutdown messages +(currently not logged). .SH EXAMPLES The following examples illustrate some of the likely usage patterns for diff --git a/pseudolog.c b/pseudolog.c index 9cf2134..aaaab28 100644 --- a/pseudolog.c +++ b/pseudolog.c @@ -35,6 +35,7 @@ #include "pseudo_db.h" static int opt_D = 0; +static int opt_U = 0; static int opt_l = 0; static void display(log_entry *, char *format); @@ -56,12 +57,14 @@ usage(int status) { "o operation (e.g. 'open')", "O order by (< DESC > ASC)", "p file path", + "P program", "r result (e.g. 'succeed')", "s timestamp", "S severity", "t type (like find -type)", "T text (text field)", "u uid", + "y type (op/ping/shutown)", NULL, }; FILE *f = (status == EXIT_SUCCESS) ? stdout : stderr; @@ -69,7 +72,8 @@ usage(int status) { fputs("pseudolog: create or report log entries. usage:\n", f); fputs("pseudolog -l [-E timeformat] [SPECIFIERS] -- create entries\n", f); - fputs("pseudolog [-D] [-F format] [-E timeformat] [SPECIFIERS] -- report entries\n", f); + fputs("pseudolog [-U] [-F format] [-E timeformat] [SPECIFIERS] -- report entries\n", f); + fputs("pseudolog -D [-E timeformat] [SPECIFIERS] -- delete entries\n", f); fputs(" format is a printf-like format string using the option letters\n", f); fputs(" listed below as format specifiers for the corresponding field.\n", f); fputs(" timeformat is a strftime-like format string, the default is '%x %X'.\n", f); @@ -85,6 +89,9 @@ usage(int status) { for (i = 0; options[i]; ++i) { fprintf(f, " %-28s%s", options[i], (i % 2) ? "\n" : " "); } + if (i % 2 == 1) { + fprintf(f, "\n"); + } exit(status); } @@ -103,11 +110,13 @@ pseudo_query_field_t opt_to_field[UCHAR_MAX + 1] = { ['O'] = PSQF_ORDER, ['p'] = PSQF_PATH, ['r'] = PSQF_RESULT, + ['R'] = PSQF_PROGRAM, ['s'] = PSQF_STAMP, ['S'] = PSQF_SEVERITY, ['t'] = PSQF_FTYPE, ['T'] = PSQF_TEXT, ['u'] = PSQF_UID, + ['y'] = PSQF_TYPE, }; pseudo_query_type_t @@ -175,7 +184,7 @@ static char *time_formats[] = { "%T", NULL, }; -static char *timeformat = "%x %X"; +static char *timeformat = "%X"; mode_t parse_file_type(char *string) { @@ -432,6 +441,9 @@ plog_trait(int opt, char *string) { return 0; } break; + case PSQF_TYPE: + new_trait->data.ivalue = pseudo_msg_type_id(string); + break; case PSQF_CLIENT: case PSQF_DEV: case PSQF_FD: @@ -464,6 +476,7 @@ plog_trait(int opt, char *string) { } break; case PSQF_PATH: /* FALLTHROUGH */ + case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ case PSQF_TAG: /* Plain strings */ @@ -495,9 +508,9 @@ main(int argc, char **argv) { int query_only = 0; int o; int bad_args = 0; - char *format = "%s %-7o %7r: [mode %04m, %2a] %p %T"; + char *format = "%s %-12.12R %-4y %7o: [mode %04m, %2a] %p %T"; - while ((o = getopt(argc, argv, "vla:c:d:DE:f:F:g:G:hi:I:m:M:o:O:p:r:s:S:t:T:u:")) != -1) { + while ((o = getopt(argc, argv, "vla:c:d:DE:f:F:g:G:hi:I:m:M:o:O:p:r:R:s:S:t:T:u:Uy:")) != -1) { switch (o) { case 'P': setenv("PSEUDO_PREFIX", optarg, 1); @@ -520,6 +533,10 @@ main(int argc, char **argv) { format = strdup(optarg); query_only = 1; break; + case 'U': + opt_U = 1; + query_only = 1; + break; case 'I': /* PSQF_ID */ query_only = 1; /* FALLTHROUGH */ @@ -536,11 +553,13 @@ main(int argc, char **argv) { case 'O': /* PSQF_ORDER */ case 'p': /* PSQF_PATH */ case 'r': /* PSQF_RESULT */ + case 'R': /* PSQF_PROGRAM */ case 's': /* PSQF_STAMP */ case 'S': /* PSQF_SEVERITY */ case 't': /* PSQF_FTYPE */ case 'T': /* PSQF_TEXT */ case 'u': /* PSQF_UID */ + case 'y': /* PSQF_TYPE */ new_trait = plog_trait(o, optarg); if (!new_trait) { bad_args = 1; @@ -595,7 +614,7 @@ main(int argc, char **argv) { pseudo_diag("couldn't parse format string (%s).\n", format); return EXIT_FAILURE; } - history = pdb_history(traits, fields, opt_D); + history = pdb_history(traits, fields, opt_U, opt_D); if (history) { log_entry *e; while ((e = pdb_history_entry(history)) != NULL) { @@ -604,8 +623,10 @@ main(int argc, char **argv) { } pdb_history_free(history); } else { - pseudo_diag("could not retrieve history.\n"); - return EXIT_FAILURE; + if (!opt_D) { + pseudo_diag("could not retrieve history.\n"); + return EXIT_FAILURE; + } } } return 0; @@ -617,7 +638,7 @@ main(int argc, char **argv) { static char * format_one(log_entry *e, char *format) { char fmtbuf[256]; - size_t len = strcspn(format, "acdfgGimMoprsStTu"), real_len; + size_t len = strcspn(format, "acdfgGimMoprRsStTuy"), real_len; char scratch[4096]; time_t stamp_sec; struct tm stamp_tm; @@ -719,6 +740,10 @@ format_one(log_entry *e, char *format) { strcpy(s, "s"); printf(fmtbuf, pseudo_res_name(e->result)); break; + case 'R': /* PSQF_PROGRAM */ + strcpy(s, "s"); + printf(fmtbuf, e->program ? e->program : ""); + break; case 's': /* PSQF_STAMP */ strcpy(s, "s"); stamp_sec = e->stamp; @@ -757,6 +782,10 @@ format_one(log_entry *e, char *format) { strcpy(s, "d"); printf(fmtbuf, (int) e->uid); break; + case 'y': /* PSQF_TYPE */ + strcpy(s, "s"); + printf(fmtbuf, pseudo_msg_type_name(e->type)); + break; } return format + len; } @@ -769,7 +798,7 @@ format_scan(char *format) { pseudo_query_field_t field; for (s = format; (s = strchr(s, '%')) != NULL; ++s) { - len = strcspn(s, "acdfgGimMoprsStTu"); + len = strcspn(s, "acdfgGimMoprRsStTuy"); s += len; if (!*s) { pseudo_diag("Unknown format: '%.3s'\n", @@ -792,10 +821,12 @@ format_scan(char *format) { case PSQF_MODE: /* FALLTHROUGH */ case PSQF_OP: /* FALLTHROUGH */ case PSQF_PATH: /* FALLTHROUGH */ + case PSQF_PROGRAM: /* FALLTHROUGH */ case PSQF_RESULT: /* FALLTHROUGH */ case PSQF_STAMP: /* FALLTHROUGH */ case PSQF_SEVERITY: /* FALLTHROUGH */ case PSQF_TEXT: /* FALLTHROUGH */ + case PSQF_TYPE: /* FALLTHROUGH */ case PSQF_UID: fields |= (1 << field); break; |