Browse Source

first pass at integrating sanity/system checks into jack1

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3434 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.117.0
paul 16 years ago
parent
commit
da01fb78df
12 changed files with 550 additions and 5 deletions
  1. +1
    -1
      Makefile.am
  2. +1
    -1
      config/os/gnu-linux/Makefile.am
  3. +73
    -0
      config/os/gnu-linux/sanitycheck.c
  4. +313
    -0
      config/os/gnu-linux/systemtest.c
  5. +12
    -0
      config/sysdeps/sanitycheck.c
  6. +12
    -0
      config/sysdeps/systemtest.c
  7. +21
    -0
      jack/sanitycheck.h
  8. +97
    -0
      jack/systemtest.h
  9. +7
    -3
      jackd/Makefile.am
  10. +11
    -0
      jackd/jackd.c
  11. +1
    -0
      libjack/sanitycheck.c
  12. +1
    -0
      libjack/systemtest.c

+ 1
- 1
Makefile.am View File

@@ -19,7 +19,7 @@ DIST_SUBDIRS = config jack libjack jackd drivers example-clients tools doc man
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = jack.pc

EXTRA_DIST = COPYING COPYING.GPL COPYING.LGPL libjack/simd.c jack.spec
EXTRA_DIST = COPYING COPYING.GPL COPYING.LGPL libjack/simd.c jack.spec testing

AUTOMAKE_OPTIONS = foreign



+ 1
- 1
config/os/gnu-linux/Makefile.am View File

@@ -1,3 +1,3 @@
MAINTAINERCLEANFILES = Makefile.in
noinst_HEADERS = time.c time.h
noinst_HEADERS = systemtest.c sanitycheck.c time.c time.h


+ 73
- 0
config/os/gnu-linux/sanitycheck.c View File

@@ -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;
}

+ 313
- 0
config/os/gnu-linux/systemtest.c View File

@@ -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;
}

+ 12
- 0
config/sysdeps/sanitycheck.c View File

@@ -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_ */

+ 12
- 0
config/sysdeps/systemtest.c View File

@@ -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_ */

+ 21
- 0
jack/sanitycheck.h View File

@@ -0,0 +1,21 @@
#ifndef __jack_sanitycheck_h__
#define __jack_sanitycheck_h__

/**
* GPL etc.
*
* @author Florian Faber
*
* @version 0.1 (2009-01-17) [FF]
* - initial version
**/

/**
* Performs a range of sanity checks on the system. The number of
* found problems is returned.
*
**/

int sanitycheck();

#endif /* __jack_sanitycheck_h__ */

+ 97
- 0
jack/systemtest.h View File

@@ -0,0 +1,97 @@
#ifndef __jack_systemtest_h__
#define __jack_systemtest_h__

/**
* GPL, yabbadabba
*
* Set of functions to gather system information for the jack setup wizard.
*
* @author Florian Faber, faber@faberman.de
*
* @version 0.1 (2009-01-15) [FF]
* - initial version
*
**/


/**
* This function checks for the existence of known frequency scaling mechanisms
* in this system.
*
* @returns 0 if the system has no frequency scaling capabilities non-0 otherwise.
**/
int system_has_frequencyscaling();


/**
* This function determines wether the CPU has a variable clock speed if frequency
* scaling is available.
*
* @returns 0 if system doesn't use frequency scaling at the moment, non-0 otherwise
**/
int system_uses_frequencyscaling();


/***
* 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 ();

/**
* Checks for the existence of the 'audio' group on this system
*
* @returns 0 is there is no 'audio' group, non-0 otherwise
**/
int system_has_audiogroup();


/**
* 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();


/**
* 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();


long long unsigned int system_memlock_amount();


/**
* Checks wether the memlock limit is unlimited
*
* @returns 0 if the memlock limit is limited, non-0 otherwise
**/
int system_memlock_is_unlimited();


long long unsigned int system_available_physical_mem();


/**
* Gets the version of the currently running kernel
*
* @returns String with the full version of the kernel
**/
char* system_kernel_version();


/**
* Returns the username. The caller is in charge of disposal of
* the returned name.
*
* @returns Pointer to a username or NULL
**/
char* system_get_username();

#endif /* __jack_systemtest_h__ */

+ 7
- 3
jackd/Makefile.am View File

@@ -44,9 +44,13 @@ lib_LTLIBRARIES = libjackserver.la

libjackserver_la_CFLAGS = $(AM_CFLAGS)

libjackserver_la_SOURCES = engine.c clientengine.c transengine.c ../libjack/client.c ../libjack/driver.c ../libjack/intclient.c \
../libjack/messagebuffer.c ../libjack/pool.c ../libjack/port.c ../libjack/midiport.c ../libjack/ringbuffer.c ../libjack/shm.c \
../libjack/thread.c ../libjack/time.c ../libjack/transclient.c ../libjack/unlock.c
libjackserver_la_SOURCES = engine.c clientengine.c transengine.c \
../libjack/systemtest.c ../libjack/sanitycheck.c \
../libjack/client.c ../libjack/driver.c ../libjack/intclient.c \
../libjack/messagebuffer.c ../libjack/pool.c ../libjack/port.c \
../libjack/midiport.c ../libjack/ringbuffer.c ../libjack/shm.c \
../libjack/thread.c ../libjack/time.c ../libjack/transclient.c \
../libjack/unlock.c
libjackserver_la_LIBADD = simd.lo @OS_LDFLAGS@
libjackserver_la_LDFLAGS = -export-dynamic -version-info @JACK_SO_VERSION@



+ 11
- 0
jackd/jackd.c View File

@@ -369,6 +369,7 @@ static void usage (FILE *file)
" [ --timeout OR -t client-timeout-in-msecs ]\n"
" [ --port-max OR -p maximum-number-of-ports]\n"
" [ --debug-timer OR -D ]\n"
" [ --no-sanity-checks OR -N ]\n"
" [ --verbose OR -v ]\n"
" [ --clocksource OR -c [ c(ycle) | h(pet) | s(ystem) ]\n"
" [ --replace-registry OR -r ]\n"
@@ -519,6 +520,7 @@ main (int argc, char *argv[])
{ "port-max", 1, 0, 'p' },
{ "no-mlock", 0, 0, 'm' },
{ "name", 1, 0, 'n' },
{ "no-sanity-checks", 1, 0, 'N' },
{ "unlock", 0, 0, 'u' },
{ "realtime", 0, 0, 'R' },
{ "replace-registry", 0, 0, 'r' },
@@ -540,6 +542,7 @@ main (int argc, char *argv[])
int driver_nargs = 1;
int show_version = 0;
int replace_registry = 0;
int do_sanity_checks = 1;
int i;
int rc;

@@ -588,6 +591,10 @@ main (int argc, char *argv[])
server_name = optarg;
break;

case 'N':
do_sanity_checks = 0;
break;

case 'p':
port_max = (unsigned int) atol (optarg);
break;
@@ -642,6 +649,10 @@ main (int argc, char *argv[])
}
}

if (do_sanity_checks && (0 < sanitycheck())) {
return -1;
}

if (show_version) {
printf ( "jackd version " VERSION
" tmpdir " DEFAULT_TMP_DIR


+ 1
- 0
libjack/sanitycheck.c View File

@@ -0,0 +1 @@
#include <sysdeps/sanitycheck.c>

+ 1
- 0
libjack/systemtest.c View File

@@ -0,0 +1 @@
#include <sysdeps/systemtest.c>

Loading…
Cancel
Save