summaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
authorpaul <paul@0c269be4-1314-0410-8aa9-9f06e86f4224>2009-03-13 16:34:09 +0000
committerpaul <paul@0c269be4-1314-0410-8aa9-9f06e86f4224>2009-03-13 16:34:09 +0000
commitda01fb78dfa01266e60aa18d012e95d53547e00b (patch)
tree7e555a40e4bac65913a1b3193c5562c35f328644 /config
parentb4c875b9fb8704a6a7e4a70491ecf1ecafc2e738 (diff)
downloadjack1-da01fb78dfa01266e60aa18d012e95d53547e00b.tar.gz
first pass at integrating sanity/system checks into jack1
git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3434 0c269be4-1314-0410-8aa9-9f06e86f4224
Diffstat (limited to 'config')
-rw-r--r--config/os/gnu-linux/Makefile.am2
-rw-r--r--config/os/gnu-linux/sanitycheck.c73
-rw-r--r--config/os/gnu-linux/systemtest.c313
-rw-r--r--config/sysdeps/sanitycheck.c12
-rw-r--r--config/sysdeps/systemtest.c12
5 files changed, 411 insertions, 1 deletions
diff --git a/config/os/gnu-linux/Makefile.am b/config/os/gnu-linux/Makefile.am
index 56c5dca..eb4c557 100644
--- a/config/os/gnu-linux/Makefile.am
+++ b/config/os/gnu-linux/Makefile.am
@@ -1,3 +1,3 @@
MAINTAINERCLEANFILES = Makefile.in
-noinst_HEADERS = time.c time.h
+noinst_HEADERS = systemtest.c sanitycheck.c time.c time.h
diff --git a/config/os/gnu-linux/sanitycheck.c b/config/os/gnu-linux/sanitycheck.c
new file mode 100644
index 0000000..523fbbc
--- /dev/null
+++ b/config/os/gnu-linux/sanitycheck.c
@@ -0,0 +1,73 @@
+/**
+ * GPL etc.
+ *
+ * @author Florian Faber
+ *
+ * @version 0.1 (2009-01-17) [FF]
+ * - initial version
+ **/
+
+#include <stdio.h>
+#include <jack/systemtest.h>
+#include <jack/sanitycheck.h>
+
+int sanitycheck() {
+ int errors = 0;
+ int relogin = 0;
+
+ if (!system_user_can_rtprio()) {
+ errors++;
+ relogin++;
+ fprintf(stderr, "\nYou are not allowed to set realtime priority.\n");
+
+ if (!system_has_rtprio_limits_conf()) {
+ errors++;
+ relogin++;
+ fprintf (stderr, "Please check your /etc/security/limits.conf for the following lines\n");
+ fprintf (stderr, "and correct/add them:\n\n");
+ fprintf(stderr, " @audio - rtprio 100\n");
+ fprintf(stderr, " @audio - nice -10\n");
+ } else if (!system_has_audiogroup()) {
+ errors++;
+ relogin++;
+ fprintf(stderr, "\nYour system has no audio group. Please add it by executing (as root):\n");
+ fprintf(stderr, " groupadd -r audio\n");
+ fprintf(stderr, " usermod -a -G audio %s\n", system_get_username());
+ } else if (!system_user_in_audiogroup()) {
+ errors++;
+ relogin++;
+ fprintf(stderr, "\nYour system has an audio group, but you are not a member of it.\n");
+ fprintf(stderr, "Please add yourself to the audio group by executing (as root):\n");
+ fprintf(stderr, " usermod -a -G audio %s\n", system_get_username());
+ }
+ }
+ if (system_has_frequencyscaling() && system_uses_frequencyscaling()) {
+ errors++;
+ fprintf(stderr, "\nYour system seems to use frequency scaling. This can have a serious impact\n");
+ fprintf(stderr, "on the audio latency. Please turn it off, e.g. by chosing the 'performance'\n");
+ fprintf(stderr, "governor.\n");
+ }
+ if (system_memlock_is_unlimited()) {
+ fprintf(stderr, "\nMemory locking is unlimited - this is dangerous. Please alter the line");
+ fprintf(stderr, " @audio - memlock unlimited");
+ fprintf(stderr, "in your /etc/limits.conf to");
+ fprintf(stderr, " @audio - memlock %llu\n", (system_available_physical_mem()*3)/4096);
+ } else if (0==system_memlock_amount()) {
+ errors++;
+ relogin++;
+ fprintf(stderr, "\nYou are not allowed to lock memory. Please add a line\n");
+ fprintf(stderr, " @audio - memlock %llu\n", (system_available_physical_mem()*3)/4096);
+ fprintf(stderr, "in your /etc/limits.conf.\n");
+ }
+
+ if (0<relogin) {
+ fprintf(stderr, "\nAfter applying these changes, please re-login in order for them to take effect.\n");
+ }
+
+ if (0<errors) {
+ fprintf(stderr, "\nYou don't appear to have a sane system configuration. It is very likely that you\n");
+ fprintf(stderr, "encounter xruns. Please apply all the above mentioned changes and start jack again!\n");
+ }
+
+ return errors;
+}
diff --git a/config/os/gnu-linux/systemtest.c b/config/os/gnu-linux/systemtest.c
new file mode 100644
index 0000000..2daf86b
--- /dev/null
+++ b/config/os/gnu-linux/systemtest.c
@@ -0,0 +1,313 @@
+/**
+ * GPL, yabbadabba
+ *
+ * Set of functions to gather system information for the jack setup wizard.
+ *
+ * TODO: Test for rt prio availability
+ *
+ * @author Florian Faber, faber@faberman.de
+ *
+ * @version 0.1 (2009-01-15) [FF]
+ * - initial version
+ *
+ **/
+
+/** maximum number of groups a user can be a member of **/
+#define MAX_GROUPS 100
+
+#include <fcntl.h>
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#include <sched.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <jack/systemtest.h>
+
+/**
+ * This function checks for the existence of known frequency scaling mechanisms
+ * in this system by testing for the availability of scaling governors/
+ *
+ * @returns 0 if the system has no frequency scaling capabilities non-0 otherwise.
+ **/
+int system_has_frequencyscaling() {
+ int fd;
+
+ fd = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors", O_RDONLY);
+
+ if (-1==fd) {
+ return 0;
+ }
+
+ (void) close(fd);
+
+ return 1;
+}
+
+
+static int read_string(char* filename, char* buf, size_t buflen) {
+ int fd;
+ ssize_t r=-1;
+
+ memset(buf, 0, buflen);
+
+ fd = open(filename, O_RDONLY);
+ if (-1<fd) {
+ r = read(fd, buf, buflen);
+ (void) close(fd);
+
+ if (-1==r) {
+ fprintf(stderr, "Error while reading \"%s\": %s\n", filename, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return (int) r;
+}
+
+
+static int read_int(char* filename, int* value) {
+ char buf[20];
+
+ if (0<read_string(filename, buf, 20)) {
+ return (1==sscanf(buf, "%d", value));
+ }
+
+ return 0;
+}
+
+
+/**
+ * This function determines wether any CPU core uses a variable clock speed if frequency
+ * scaling is available. If the governor for all cores is either "powersave" or
+ * "performance", the CPU frequency can be assumed to be static. This is also the case
+ * if scaling_min_freq and scaling_max_freq are set to the same value.
+ *
+ * @returns 0 if system doesn't use frequency scaling at the moment, non-0 otherwise
+ **/
+int system_uses_frequencyscaling() {
+ int cpu=0, done=0, min, max;
+ char filename[256], buf[256];
+
+ while (!done) {
+ (void) snprintf(filename, 256, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
+ if (0<read_string(filename, buf, 256)) {
+ if ((0!=strcmp("performance", buf)) &&
+ (0!=strcmp("powersafe", buf))) {
+ // So it's neither the "performance" nor the "powersafe" governor
+ (void) snprintf(filename, 256, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
+ if (read_int(filename, &min)) {
+ (void) snprintf(filename, 256, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
+ if (read_int(filename, &max)) {
+ if (min!=max) {
+ // wrong governor AND different frequency limits -> scaling
+ return 1;
+ }
+ }
+ }
+ }
+ } else {
+ // couldn't open file -> no more cores
+ done = 1;
+ }
+ cpu++;
+ }
+
+ // couldn't find anything that points to scaling
+ return 0;
+}
+
+
+static gid_t get_group_by_name(const char* name) {
+ struct group* grp;
+ gid_t res = 0;
+
+ while ((0==res) && (NULL != (grp = getgrent()))) {
+ if (0==strcmp(name, grp->gr_name)) {
+ res = grp->gr_gid;
+ }
+ }
+
+ endgrent();
+
+ return res;
+}
+
+/***
+ * Checks for a definition in /etc/security/limits.conf that looks
+ * as if it allows RT scheduling priority.
+ *
+ * @returns 1 if there appears to be such a line
+ **/
+int system_has_rtprio_limits_conf ()
+{
+ const char* limits = "/etc/security/limits.conf";
+ char cmd[100];
+
+ snprintf (cmd, sizeof (cmd), "grep -q 'rtprio *[0-9][0-9]*' %s", limits);
+ if (system (cmd) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * Checks for the existence of the 'audio' group on this system
+ *
+ * @returns 0 is there is no 'audio' group, the group id otherwise
+ **/
+int system_has_audiogroup() {
+ return get_group_by_name("audio") || get_group_by_name ("jackuser");
+}
+
+
+/**
+ * Tests wether the owner of this process is in the 'audio' group.
+ *
+ * @returns 0 if the owner of this process is not in the audio group, non-0 otherwise
+ **/
+int system_user_in_audiogroup() {
+ gid_t* list = (gid_t*) malloc(MAX_GROUPS * sizeof(gid_t));
+ int num_groups, i=0, found=0;
+ unsigned int gid;
+
+ if (NULL==list) {
+ perror("Cannot allocate group list structure");
+ exit(EXIT_FAILURE);
+ }
+
+ gid = get_group_by_name("audio");
+ if (0==gid) {
+ fprintf(stderr, "No audio group found\n");
+ exit(EXIT_FAILURE);
+ }
+
+ num_groups = getgroups(MAX_GROUPS, list);
+
+ while (i<num_groups) {
+ if (list[i]==gid) {
+ found = 1;
+ i = num_groups;
+ }
+
+ i++;
+ }
+
+ free(list);
+
+ return found;
+}
+
+
+/**
+ * Determines wether the owner of this process can enable rt priority.
+ *
+ * @returns 0 if this process can not be switched to rt prio, non-0 otherwise
+ **/
+int system_user_can_rtprio() {
+ int min_prio;
+ struct sched_param schparam;
+
+ memset(&schparam, 0, sizeof(struct sched_param));
+
+ if (-1 == (min_prio = sched_get_priority_min(SCHED_RR))) {
+ perror("sched_get_priority");
+ exit(EXIT_FAILURE);
+ }
+ schparam.sched_priority = min_prio;
+
+ if (0 == sched_setscheduler(0, SCHED_RR, &schparam)) {
+ // TODO: restore previous state
+ schparam.sched_priority = 0;
+ if (0 != sched_setscheduler(0, SCHED_OTHER, &schparam)) {
+ perror("sched_setscheduler");
+ exit(EXIT_FAILURE);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+
+long long unsigned int system_memlock_amount() {
+ struct rlimit limits;
+
+ if (-1==getrlimit(RLIMIT_MEMLOCK, &limits)) {
+ perror("getrlimit on RLIMIT_MEMLOCK");
+ exit(EXIT_FAILURE);
+ }
+
+ return limits.rlim_max;
+}
+
+
+/**
+ * Checks wether the memlock limit is unlimited
+ *
+ * @returns - 0 if the memlock limit is limited, non-0 otherwise
+ **/
+int system_memlock_is_unlimited() {
+ return ((RLIM_INFINITY==system_memlock_amount())?1:0);
+}
+
+
+long long unsigned int system_available_physical_mem() {
+ int fd, i;
+ char buf[256];
+ long long unsigned int res = 0;
+
+ fd = open("/proc/meminfo", O_RDONLY);
+
+ if (0<fd) {
+ if (0<read(fd, buf, 256)) {
+ if (strcmp("MemTotal:", buf)) {
+ i=10;
+ while (buf[i]==' ') i++;
+ (void) sscanf(&buf[i], "%llu", &res);
+ }
+ } else {
+ perror("read from /proc/meminfo");
+ }
+
+ (void) close(fd);
+ } else {
+ perror("open /proc/meminfo");
+ }
+
+ return res*1024;
+}
+
+
+/**
+ * Gets the version of the currently running kernel. The string
+ * returned has to be freed by the caller.
+ *
+ * @returns String with the full version of the kernel
+ **/
+char* system_kernel_version() {
+ return NULL;
+}
+
+
+
+char* system_get_username() {
+ char* res = NULL;
+ char* name = NULL;
+
+ if ((name = getlogin())) {
+ res = strdup(name);
+ }
+
+ return res;
+}
diff --git a/config/sysdeps/sanitycheck.c b/config/sysdeps/sanitycheck.c
new file mode 100644
index 0000000..fb8359e
--- /dev/null
+++ b/config/sysdeps/sanitycheck.c
@@ -0,0 +1,12 @@
+#ifndef _jack_sysdep_sanitycheck_c_
+#define _jack_sysdep_sanitycheck_c_
+
+#if defined(__gnu_linux__)
+#include <config/os/gnu-linux/sanitycheck.c>
+#elif defined(__MACH__) && defined(__APPLE__)
+/* relax */
+#else
+/* relax */
+#endif
+
+#endif /* _jack_sysdep_sanitycheck_c_ */
diff --git a/config/sysdeps/systemtest.c b/config/sysdeps/systemtest.c
new file mode 100644
index 0000000..7afa3ec
--- /dev/null
+++ b/config/sysdeps/systemtest.c
@@ -0,0 +1,12 @@
+#ifndef _jack_sysdep_systemtest_c_
+#define _jack_sysdep_systemtest_c_
+
+#if defined(__gnu_linux__)
+#include <config/os/gnu-linux/systemtest.c>
+#elif defined(__MACH__) && defined(__APPLE__)
+/* relax */
+#else
+/* relax */
+#endif
+
+#endif /* _jack_sysdep_systemtest_c_ */