disobedience/manual.html
disobedience/images.h
debian/disorder-server
+doc/disorder-stats.8
+doc/disorder-stats.8.html
+server/disorder-stats
SEDFILES=disorder.1 disorderd.8 disorder_config.5 \
disorder-dump.8 disorder_protocol.5 disorder-deadlock.8 \
disorder-rescan.8 disobedience.1 disorderfm.1 disorder-playrtp.1 \
- disorder-decode.8
+ disorder-decode.8 disorder-stats.8
include ${top_srcdir}/scripts/sedfiles.make
man_MANS=disorderd.8 disorder.1 disorder.3 disorder_config.5 disorder-dump.8 \
disorder_protocol.5 disorder-deadlock.8 \
disorder-rescan.8 disobedience.1 disorderfm.1 disorder-speaker.8 \
- disorder-playrtp.1 disorder-normalize.8 disorder-decode.8
+ disorder-playrtp.1 disorder-normalize.8 disorder-decode.8 \
+ disorder-stats.8
noinst_MANS=tkdisorder.1
disorder.3 disorder-dump.8.in disorder_protocol.5.in \
tkdisorder.1 disorder-deadlock.8.in disorder-rescan.8.in \
disobedience.1.in disorderfm.1.in disorder-speaker.8 \
- disorder-playrtp.1.in disorder-decode.8.in disorder-normalize.8
+ disorder-playrtp.1.in disorder-decode.8.in disorder-normalize.8 \
+ disorder-stats.8.in
CLEANFILES=$(SEDFILES) $(HTMLMAN)
--- /dev/null
+.\"
+.\" Copyright (C) 2007 Richard Kettlewell
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+.\" USA
+.\"
+.TH disorder-stats 8
+.SH NAME
+disorder-stats \- DisOrder statistics
+.SH SYNOPSIS
+.B disorder-stats
+.RI [ OPTIONS ]
+.SH DESCRIPTION
+.B disorder-stats
+reports server statistics. It is used by the server and would not
+normally be invoked manually.
+.SH OPTIONS
+.TP
+.B --config \fIPATH\fR, \fB-c \fIPATH
+Set the configuration file.
+.TP
+.B --debug\fR, \fB-d
+Enable debugging.
+.TP
+.B --help\fR, \fB-h
+Display a usage message.
+.TP
+.B --version\fR, \fB-V
+Display version number.
+.SH "SEE ALSO"
+\fBdisorderd\fR(8), \fBdisorder_config\fR(5)
+.\" Local Variables:
+.\" mode:nroff
+.\" End:
#
sbin_PROGRAMS=disorderd disorder-deadlock disorder-rescan disorder-dump \
- disorder-speaker disorder-decode disorder-normalize
+ disorder-speaker disorder-decode disorder-normalize \
+ disorder-stats
noinst_PROGRAMS=disorder.cgi trackname
noinst_DATA=uk.org.greenend.rjk.disorder.plist
disorder_rescan_LDFLAGS=-export-dynamic
disorder_rescan_DEPENDENCIES=../lib/libdisorder.a
+disorder_stats_SOURCES=stats.c trackdb.c trackdb.h
+disorder_stats_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
+ $(LIBDB) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT)
+disorder_stats_DEPENDENCIES=../lib/libdisorder.a
+
disorder_dump_SOURCES=dump.c \
trackdb.c trackdb.h \
../lib/memgc.c
return 1; /* completed */
}
+static void got_stats(char *stats, void *u) {
+ struct conn *const c = u;
+
+ sink_printf(ev_writer_sink(c->w), "253 stats\n%s\n.\n", stats);
+ /* Now we can start processing commands again */
+ ev_reader_enable(c->r);
+}
+
static int c_stats(struct conn *c,
char attribute((unused)) **vec,
int attribute((unused)) nvec) {
- char **v;
- int nv, n;
-
- v = trackdb_stats(&nv);
- sink_printf(ev_writer_sink(c->w), "253 stats\n");
- for(n = 0; n < nv; ++n) {
- if(v[n][0] == '.')
- sink_writes(ev_writer_sink(c->w), ".");
- sink_printf(ev_writer_sink(c->w), "%s\n", v[n]);
- }
- sink_writes(ev_writer_sink(c->w), ".\n");
- return 1;
+ trackdb_stats_subprocess(c->ev, got_stats, c);
+ return 0; /* not yet complete */
}
static int c_volume(struct conn *c,
--- /dev/null
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <config.h>
+#include "types.h"
+
+#include <locale.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pcre.h>
+#include <getopt.h>
+
+#include "defs.h"
+#include "mem.h"
+#include "log.h"
+#include "syscalls.h"
+#include "configuration.h"
+#include "trackdb.h"
+
+static const struct option options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V' },
+ { "config", required_argument, 0, 'c' },
+ { "debug", no_argument, 0, 'd' },
+ { "no-debug", no_argument, 0, 'D' },
+ { 0, 0, 0, 0 }
+};
+
+/* display usage message and terminate */
+static void help(void) {
+ xprintf("Usage:\n"
+ " disorder-stats [OPTIONS]\n"
+ "Options:\n"
+ " --help, -h Display usage message\n"
+ " --version, -V Display version number\n"
+ " --config PATH, -c PATH Set configuration file\n"
+ " --[no-]debug, -d Turn on (off) debugging\n"
+ "\n"
+ "Generate DisOrder database statistics.\n");
+ xfclose(stdout);
+ exit(0);
+}
+
+/* display version number and terminate */
+static void version(void) {
+ xprintf("disorder-stats version %s\n", disorder_version_string);
+ xfclose(stdout);
+ exit(0);
+}
+
+int main(int argc, char **argv) {
+ int n;
+ char **stats;
+
+ set_progname(argv);
+ mem_init();
+ if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+ while((n = getopt_long(argc, argv, "hVc:dD", options, 0)) >= 0) {
+ switch(n) {
+ case 'h': help();
+ case 'V': version();
+ case 'c': configfile = optarg; break;
+ case 'd': debugging = 1; break;
+ case 'D': debugging = 0; break;
+ default: fatal(0, "invalid option");
+ }
+ }
+ if(config_read(0))
+ fatal(0, "cannot read configuration");
+ trackdb_init(0);
+ trackdb_open();
+ stats = trackdb_stats(0);
+ while(*stats)
+ xprintf("%s\n", *stats++);
+ xfclose(stdout);
+ return 0;
+}
+
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
return 0;
}
-static pid_t subprogram(ev_source *ev, const char *prog) {
+static pid_t subprogram(ev_source *ev, const char *prog,
+ int outputfd) {
pid_t pid;
int lfd;
if(lfd != -1) {
xdup2(lfd, 1);
xdup2(lfd, 2);
+ xclose(lfd);
+ }
+ if(outputfd != -1) {
+ xdup2(outputfd, 1);
+ xclose(outputfd);
}
/* If we were negatively niced, undo it. We don't bother checking for
* error, it's not that important. */
/* start deadlock manager */
void trackdb_master(ev_source *ev) {
assert(db_deadlock_pid == -1);
- db_deadlock_pid = subprogram(ev, DEADLOCK);
+ db_deadlock_pid = subprogram(ev, DEADLOCK, -1);
ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0);
D(("started deadlock manager"));
}
char **trackdb_stats(int *nstatsp) {
DB_TXN *tid;
struct vector v;
- char *s;
vector_init(&v);
for(;;) {
if(get_stats(&v, trackdb_prefsdb, SI(hash), tid)) goto fail;
vector_append(&v, (char *)"");
if(search_league(&v, 10, tid)) goto fail;
- vector_append(&v, (char *)"");
- vector_append(&v, (char *)"Server stats:");
- byte_xasprintf(&s, "track lookup cache hits: %lu", cache_files_hits);
- vector_append(&v, (char *)s);
- byte_xasprintf(&s, "track lookup cache misses: %lu", cache_files_misses);
- vector_append(&v, (char *)s);
vector_terminate(&v);
break;
fail:
return v.vec;
}
+struct stats_details {
+ void (*done)(char *data, void *u);
+ void *u;
+ int exited; /* subprocess exited */
+ int closed; /* pipe close */
+ int wstat; /* wait status from subprocess */
+ struct dynstr data[1]; /* data read from pipe */
+};
+
+static void stats_complete(struct stats_details *d) {
+ char *s;
+
+ if(!(d->exited && d->closed))
+ return;
+ byte_xasprintf(&s, "\n"
+ "Server stats:\n"
+ "track lookup cache hits: %lu\n"
+ "track lookup cache misses: %lu\n",
+ cache_files_hits,
+ cache_files_misses);
+ dynstr_append_string(d->data, s);
+ dynstr_terminate(d->data);
+ d->done(d->data->vec, d->u);
+}
+
+static int stats_finished(ev_source attribute((unused)) *ev,
+ pid_t attribute((unused)) pid,
+ int status,
+ const struct rusage attribute((unused)) *rusage,
+ void *u) {
+ struct stats_details *const d = u;
+
+ d->exited = 1;
+ if(status)
+ error(0, "disorder-stats %s", wstat(status));
+ stats_complete(d);
+ return 0;
+}
+
+static int stats_read(ev_source attribute((unused)) *ev,
+ ev_reader *reader,
+ int attribute((unused)) fd,
+ void *ptr,
+ size_t bytes,
+ int eof,
+ void *u) {
+ struct stats_details *const d = u;
+
+ dynstr_append_bytes(d->data, ptr, bytes);
+ ev_reader_consume(reader, bytes);
+ if(eof)
+ d->closed = 1;
+ stats_complete(d);
+ return 0;
+}
+
+static int stats_error(ev_source attribute((unused)) *ev,
+ int attribute((unused)) fd,
+ int errno_value,
+ void *u) {
+ struct stats_details *const d = u;
+
+ error(errno_value, "error reading from pipe to disorder-stats");
+ d->closed = 1;
+ stats_complete(d);
+ return 0;
+}
+
+void trackdb_stats_subprocess(ev_source *ev,
+ void (*done)(char *data, void *u),
+ void *u) {
+ int p[2];
+ pid_t pid;
+ struct stats_details *d = xmalloc(sizeof *d);
+
+ dynstr_init(d->data);
+ d->done = done;
+ d->u = u;
+ xpipe(p);
+ pid = subprogram(ev, "disorder-stats", p[1]);
+ xclose(p[1]);
+ ev_child(ev, pid, 0, stats_finished, d);
+ ev_reader_new(ev, p[0], stats_read, stats_error, d, "disorder-stats reader");
+}
+
/* set a pref (remove if value=0) */
int trackdb_set(const char *track,
const char *name,
error(0, "rescan already underway");
return;
}
- rescan_pid = subprogram(ev, RESCAN);
+ rescan_pid = subprogram(ev, RESCAN, -1);
ev_child(ev, rescan_pid, 0, reap_rescan, 0);
D(("started rescanner"));
char **trackdb_stats(int *nstatsp);
/* return a list of database stats */
+void trackdb_stats_subprocess(struct ev_source *ev,
+ void (*done)(char *data, void *u),
+ void *u);
+/* collect stats in background and call done() with results */
+
int trackdb_set(const char *track,
const char *name,
const char *value);