diff --git a/Makefile b/Makefile index 8372404..75b9ab8 100644 --- a/Makefile +++ b/Makefile @@ -16,10 +16,12 @@ rpms: rpmbuild --define "_rpmdir rpm/" --define "_builddir ." -bb rpm_spec/qubes-utils.spec all: $(MAKE) -C qrexec-lib all + $(MAKE) -C qmemman all install: $(MAKE) -C udev install $(MAKE) -C qrexec-lib install + $(MAKE) -C qmemman install clean: $(MAKE) -C qrexec-lib clean diff --git a/qmemman/Makefile b/qmemman/Makefile new file mode 100644 index 0000000..2c1da6a --- /dev/null +++ b/qmemman/Makefile @@ -0,0 +1,11 @@ +CC=gcc +CFLAGS=-Wall -g -O3 +all: meminfo-writer +meminfo-writer: meminfo-writer.o + $(CC) -g -o meminfo-writer meminfo-writer.o -lxenstore +install: + install -D meminfo-writer $(DESTDIR)/usr/sbin/meminfo-writer + install -d $(DESTDIR)/usr/lib/systemd/system/ + install qubes-meminfo-writer*service $(DESTDIR)/usr/lib/systemd/system/ +clean: + rm -f meminfo-writer xenstore-watch *.o diff --git a/qmemman/meminfo-writer.c b/qmemman/meminfo-writer.c new file mode 100644 index 0000000..f3563b1 --- /dev/null +++ b/qmemman/meminfo-writer.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned long prev_used_mem; +int used_mem_change_threshold; +int delay; +int usr1_received; + +char *parse(char *buf) +{ + char *ptr = buf; + char name[256]; + static char outbuf[4096]; + int val; + int len; + int MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapTotal = + 0, SwapFree = 0; + unsigned long long key; + long used_mem, used_mem_diff; + int nitems = 0; + + while (nitems != 6) { + sscanf(ptr, "%s %d kB\n%n", name, &val, &len); + key = *(unsigned long long *) ptr; + if (key == *(unsigned long long *) "MemTotal:") { + MemTotal = val; + nitems++; + } else if (key == *(unsigned long long *) "MemFree:") { + MemFree = val; + nitems++; + } else if (key == *(unsigned long long *) "Buffers:") { + Buffers = val; + nitems++; + } else if (key == *(unsigned long long *) "Cached: ") { + Cached = val; + nitems++; + } else if (key == *(unsigned long long *) "SwapTotal:") { + SwapTotal = val; + nitems++; + } else if (key == *(unsigned long long *) "SwapFree:") { + SwapFree = val; + nitems++; + } + + ptr += len; + } + + used_mem = + MemTotal - Buffers - Cached - MemFree + SwapTotal - SwapFree; + if (used_mem < 0) + return NULL; + + used_mem_diff = used_mem - prev_used_mem; + if (used_mem_diff < 0) + used_mem_diff = -used_mem_diff; + if (used_mem_diff > used_mem_change_threshold + || (used_mem > prev_used_mem && used_mem * 13 / 10 > MemTotal + && used_mem_diff > used_mem_change_threshold/2)) { + prev_used_mem = used_mem; + sprintf(outbuf, + "MemTotal: %d kB\nMemFree: %d kB\nBuffers: %d kB\nCached: %d kB\n" + "SwapTotal: %d kB\nSwapFree: %d kB\n", MemTotal, + MemFree, Buffers, Cached, SwapTotal, SwapFree); + return outbuf; + } + return NULL; +} + +void usage() +{ + fprintf(stderr, + "usage: meminfo_writer threshold_in_kb delay_in_us [pidfile]\n"); + fprintf(stderr, " When pidfile set, meminfo-writer will:\n"); + fprintf(stderr, " - fork into background\n"); + fprintf(stderr, " - wait for SIGURS1 (in background) before starting main work\n"); + exit(1); +} + +void send_to_qmemman(struct xs_handle *xs, char *data) +{ + if (!xs_write(xs, XBT_NULL, "memory/meminfo", data, strlen(data))) { + syslog(LOG_DAEMON | LOG_ERR, "error writing xenstore ?"); + exit(1); + } +} + +void usr1_handler(int sig) { + usr1_received = 1; +} + +int main(int argc, char **argv) +{ + char buf[4096]; + int n; + char *meminfo_data; + int fd; + struct xs_handle *xs; + + if (argc != 3 && argc != 4) + usage(); + used_mem_change_threshold = atoi(argv[1]); + delay = atoi(argv[2]); + if (!used_mem_change_threshold || !delay) + usage(); + + if (argc == 4) { + pid_t pid; + sigset_t mask, oldmask; + + switch (pid = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + sigemptyset (&mask); + sigaddset (&mask, SIGUSR1); + /* Wait for a signal to arrive. */ + sigprocmask (SIG_BLOCK, &mask, &oldmask); + usr1_received = 0; + signal(SIGUSR1, usr1_handler); + while (!usr1_received) + sigsuspend (&oldmask); + sigprocmask (SIG_UNBLOCK, &mask, NULL); + break; + default: + fd = open(argv[3], O_CREAT | O_TRUNC | O_WRONLY, 0666); + if (fd < 0) { + perror("open pidfile"); + exit(1); + } + n = sprintf(buf, "%d\n", pid); + if (write(fd, buf, n) != n) { + perror("write pid"); + exit(1); + } + close(fd); + exit(0); + } + } + + fd = open("/proc/meminfo", O_RDONLY); + if (fd < 0) { + perror("open meminfo"); + exit(1); + } + xs = xs_domain_open(); + if (!xs) { + perror("xs_domain_open"); + exit(1); + } + if (argc == 3) { + /* if not waiting for signal, fork after first info written to xenstore */ + n = pread(fd, buf, sizeof(buf), 0); + buf[n] = 0; + meminfo_data = parse(buf); + if (meminfo_data) + send_to_qmemman(xs, meminfo_data); + if (fork() > 0) + exit(0); + } + + for (;;) { + n = pread(fd, buf, sizeof(buf), 0); + buf[n] = 0; + meminfo_data = parse(buf); + if (meminfo_data) + send_to_qmemman(xs, meminfo_data); + usleep(delay); + } +} diff --git a/qmemman/qubes-meminfo-writer-dom0.service b/qmemman/qubes-meminfo-writer-dom0.service new file mode 100644 index 0000000..859d4c3 --- /dev/null +++ b/qmemman/qubes-meminfo-writer-dom0.service @@ -0,0 +1,13 @@ +[Unit] +Description=Qubes memory information reporter +After=qubes-core.service qubes-qmemman.service +ConditionPathExists=/var/run/qubes/qmemman.sock + +[Service] +Type=simple +ExecStart=/usr/sbin/meminfo-writer 30000 100000 +StandardOutput=syslog +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/qmemman/qubes-meminfo-writer.service b/qmemman/qubes-meminfo-writer.service new file mode 100644 index 0000000..971cf83 --- /dev/null +++ b/qmemman/qubes-meminfo-writer.service @@ -0,0 +1,12 @@ +[Unit] +Description=Qubes memory information reporter +ConditionPathExists=/var/run/qubes-service/meminfo-writer + +[Service] +Type=forking +ExecStart=/usr/sbin/meminfo-writer 30000 100000 /var/run/meminfo-writer.pid +PIDFile=/var/run/meminfo-writer.pid +StandardOutput=syslog + +[Install] +WantedBy=multi-user.target diff --git a/rpm_spec/qubes-utils.spec b/rpm_spec/qubes-utils.spec index 8ed1c42..3d56c98 100644 --- a/rpm_spec/qubes-utils.spec +++ b/rpm_spec/qubes-utils.spec @@ -34,6 +34,21 @@ make all %install make install DESTDIR=%{buildroot} +%post +if [ -r /etc/qubes-release ]; then + # dom0 + /bin/systemctl enable qubes-meminfo-writer-dom0.service > /dev/null 2>&1 +else + # VM + /bin/systemctl enable qubes-meminfo-writer.service > /dev/null 2>&1 +fi + +%postun +if [ $1 -eq 0 ]; then + /bin/systemctl disable qubes-meminfo-writer.service > /dev/null 2>&1 + /bin/systemctl disable qubes-meminfo-writer.service > /dev/null 2>&1 +fi + %clean rm -rf $RPM_BUILD_ROOT @@ -41,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root,-) /etc/udev/rules.d/99-qubes-*.rules /usr/libexec/qubes/udev-* +%{_sbindir}/meminfo-writer +%{_unitdir}/qubes-meminfo-writer.service +%{_unitdir}/qubes-meminfo-writer-dom0.service %files devel