/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * List of system drivers for the Unix library. * * By Michael Bukin. * * See readme.txt for copyright information. */ #include #include #include #include #include #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintunix.h" #if defined(ALLEGRO_USE_SCHED_YIELD) && defined(_POSIX_PRIORITY_SCHEDULING) /* ALLEGRO_USE_SCHED_YIELD is set by configure */ /* Manpages say systems providing sched_yield() define * _POSIX_PRIORITY_SCHEDULING in unistd.h */ #include #else #include #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef ALLEGRO_HAVE_SV_PROCFS #include #include #include #endif /* list the available drivers */ _DRIVER_INFO _system_driver_list[] = { #ifdef ALLEGRO_WITH_XWINDOWS { SYSTEM_XWINDOWS, &system_xwin, TRUE }, #endif #ifdef ALLEGRO_LINUX { SYSTEM_LINUX, &system_linux, TRUE }, #endif #ifdef ALLEGRO_QNX { SYSTEM_QNX, &system_qnx, TRUE }, #endif #ifdef ALLEGRO_MACOSX { SYSTEM_MACOSX, &system_macosx, TRUE }, #endif { SYSTEM_NONE, &system_none, FALSE }, { 0, NULL, 0 } }; #ifndef ALLEGRO_MACOSX /* background function manager */ struct bg_manager *_unix_bg_man; #endif /* _unix_find_resource: * Helper for locating a Unix config file. Looks in the home directory * of the current user, and in /etc. */ int _unix_find_resource(char *dest, AL_CONST char *resource, int size) { char buf[256], tmp[256], *last; char *home = getenv("HOME"); if (home) { /* look for ~/file */ append_filename(buf, uconvert_ascii(home, tmp), resource, sizeof(buf)); if (exists(buf)) { ustrzcpy(dest, size, buf); return 0; } /* if it is a .cfg, look for ~/.filerc */ if (ustricmp(get_extension(resource), uconvert_ascii("cfg", tmp)) == 0) { ustrzcpy(buf, sizeof(buf) - ucwidth(OTHER_PATH_SEPARATOR), uconvert_ascii(home, tmp)); put_backslash(buf); ustrzcat(buf, sizeof(buf), uconvert_ascii(".", tmp)); ustrzcpy(tmp, sizeof(tmp), resource); ustrzcat(buf, sizeof(buf), ustrtok_r(tmp, ".", &last)); ustrzcat(buf, sizeof(buf), uconvert_ascii("rc", tmp)); if (file_exists(buf, FA_ARCH | FA_RDONLY | FA_HIDDEN, NULL)) { ustrzcpy(dest, size, buf); return 0; } } } /* look for /etc/file */ append_filename(buf, uconvert_ascii("/etc/", tmp), resource, sizeof(buf)); if (exists(buf)) { ustrzcpy(dest, size, buf); return 0; } /* if it is a .cfg, look for /etc/filerc */ if (ustricmp(get_extension(resource), uconvert_ascii("cfg", tmp)) == 0) { ustrzcpy(buf, sizeof(buf), uconvert_ascii("/etc/", tmp)); ustrzcpy(tmp, sizeof(tmp), resource); ustrzcat(buf, sizeof(buf), ustrtok_r(tmp, ".", &last)); ustrzcat(buf, sizeof(buf), uconvert_ascii("rc", tmp)); if (exists(buf)) { ustrzcpy(dest, size, buf); return 0; } } /* if it is a .dat, look in /usr/share/ and /usr/local/share/ */ if (ustricmp(get_extension(resource), uconvert_ascii("dat", tmp)) == 0) { ustrzcpy(buf, sizeof(buf), uconvert_ascii("/usr/share/allegro/", tmp)); ustrzcat(buf, sizeof(buf), resource); if (exists(buf)) { ustrzcpy(dest, size, buf); return 0; } ustrzcpy(buf, sizeof(buf), uconvert_ascii("/usr/local/share/allegro/", tmp)); ustrzcat(buf, sizeof(buf), resource); if (exists(buf)) { ustrzcpy(dest, size, buf); return 0; } } return -1; } #ifndef ALLEGRO_MACOSX /* _unix_read_os_type: * Set the os_type variable to something sensible. */ void _unix_read_os_type(void) { #ifdef HAVE_SYS_UTSNAME_H struct utsname utsn; char *tmpstr, *tmpstr2; size_t pos; uname(&utsn); /* fetch OS version and revision */ tmpstr = malloc(strlen(utsn.release)+1); _al_sane_strncpy(tmpstr, utsn.release, strlen(utsn.release)+1); tmpstr2 = NULL; for (pos = 0; pos <= strlen(utsn.release); pos++) { if (tmpstr[pos] == '.') { tmpstr[pos] = '\0'; if (!tmpstr2) tmpstr2 = tmpstr + pos + 1; } } os_version = atoi(tmpstr); os_revision = atoi(tmpstr2); free(tmpstr); /* try to detect Unix systems we know of */ if (!strcmp(utsn.sysname, "Linux")) { os_type = OSTYPE_LINUX; } else if (!strcmp(utsn.sysname, "SunOS")) { os_type = OSTYPE_SUNOS; } else if (!strcmp(utsn.sysname, "FreeBSD")) { os_type = OSTYPE_FREEBSD; } else if (!strcmp(utsn.sysname, "NetBSD")) { os_type = OSTYPE_NETBSD; } else if ((!strcmp(utsn.sysname, "IRIX")) || (!strcmp(utsn.sysname, "IRIX64"))) { os_type = OSTYPE_IRIX; } else if (!strcmp(utsn.sysname, "Darwin")) { os_type = OSTYPE_DARWIN; } else if (!strcmp(utsn.sysname, "QNX")) { os_type = OSTYPE_QNX; } else { os_type = OSTYPE_UNIX; /* that's all we can say for now */ } #else os_type = OSTYPE_UNIX; #endif os_multitasking = TRUE; } #endif /* _unix_sysdrv_yield_timeslice: * Yields remaining timeslice portion to the system */ void _unix_yield_timeslice(void) { #if defined(ALLEGRO_USE_SCHED_YIELD) && defined(_POSIX_PRIORITY_SCHEDULING) sched_yield(); #else struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; select(0, NULL, NULL, NULL, &timeout); #endif } #ifndef ALLEGRO_MACOSX /* _find_executable_file: * Helper function: searches path and current directory for executable. * Returns 1 on succes, 0 on failure. */ static int _find_executable_file(const char *filename, char *output, int size) { char *path; /* If filename has an explicit path, search current directory */ if (strchr (filename, '/')) { if (filename[0] == '/') { /* Full path; done */ do_uconvert (filename, U_ASCII, output, U_CURRENT, size); return 1; } else { struct stat finfo; char pathname[1024]; int len; /* Prepend current directory */ getcwd(pathname, sizeof(pathname)); len = strlen(pathname); pathname[len] = '/'; _al_sane_strncpy (pathname+len+1, filename, strlen(filename)); if ((stat(pathname, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) { do_uconvert (pathname, U_ASCII, output, U_CURRENT, size); return 1; } } } /* If filename has no explicit path, but we do have $PATH, search there */ else if ((path = getenv("PATH"))) { char *start = path, *end = path, *buffer = NULL, *temp; struct stat finfo; while (*end) { end = strchr (start, ':'); if (!end) end = strchr (start, '\0'); /* Resize `buffer' for path component, slash, filename and a '\0' */ temp = realloc (buffer, end - start + 1 + strlen (filename) + 1); if (temp) { buffer = temp; _al_sane_strncpy (buffer, start, end - start); *(buffer + (end - start)) = '/'; _al_sane_strncpy (buffer + (end - start) + 1, filename, end - start + 1 + strlen (filename) + 1); if ((stat(buffer, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) { do_uconvert (buffer, U_ASCII, output, U_CURRENT, size); free (buffer); return 1; } } /* else... ignore the failure; `buffer' is still valid anyway. */ start = end + 1; } /* Path search failed */ free (buffer); } return 0; } /* _unix_get_executable_name: * Return full path to the current executable, use proc fs if available. */ void _unix_get_executable_name(char *output, int size) { #ifdef ALLEGRO_HAVE_SV_PROCFS struct prpsinfo psinfo; int fd; #endif #if defined ALLEGRO_HAVE_SV_PROCFS || defined ALLEGRO_SYS_GETEXECNAME char *s; #endif char linkname[1024]; char filename[1024]; struct stat finfo; FILE *pipe; pid_t pid; int len; #ifdef ALLEGRO_HAVE_GETEXECNAME s = getexecname(); if (s) { if (s[0] == '/') { /* Absolute path */ do_uconvert (s, U_ASCII, output, U_CURRENT, size); return; } else { /* Not an absolute path */ if (_find_executable_file(s, output, size)) return; } } s = NULL; #endif /* We need the PID in order to query procfs */ pid = getpid(); /* Try a Linux-like procfs */ /* get symolic link to executable from proc fs */ sprintf (linkname, "/proc/%d/exe", pid); if (stat (linkname, &finfo) == 0) { len = readlink (linkname, filename, sizeof(filename)-1); if (len>-1) { filename[len] = '\0'; do_uconvert (filename, U_ASCII, output, U_CURRENT, size); return; } } /* Use System V procfs calls if available */ #ifdef ALLEGRO_HAVE_SV_PROCFS sprintf (linkname, "/proc/%d/exe", pid); fd = open(linkname, O_RDONLY); if (!fd == -1) { ioctl(fd, PIOCPSINFO, &psinfo); close(fd); /* Use argv[0] directly if we can */ #ifdef ALLEGRO_HAVE_PROCFS_ARGCV if (psinfo.pr_argv && psinfo.pr_argc) { if (_find_executable_file(psinfo.pr_argv[0], output, size)) return; } else #endif { /* Emulate it */ /* We use the pr_psargs field to find argv[0] * This is better than using the pr_fname field because we need * the additional path information that may be present in argv[0] */ /* Skip other args */ s = strchr(psinfo.pr_psargs, ' '); if (s) s[0] = '\0'; if (_find_executable_file(psinfo.pr_psargs, output, size)) return; } /* Try the pr_fname just for completeness' sake if argv[0] fails */ if (_find_executable_file(psinfo.pr_fname, output, size)) return; } #endif /* Last resort: try using the output of the ps command to at least find */ /* the name of the file if not the full path */ uszprintf (linkname, sizeof(linkname), "ps -p %d", pid); do_uconvert (linkname, U_CURRENT, filename, U_ASCII, size); pipe = popen(filename, "r"); if (pipe) { /* The first line of output is a header */ fgets(linkname, sizeof(linkname), pipe); /* The information we want is in the last column; find it */ len = strlen(linkname); while (linkname[len] != ' ' && linkname[len] != '\t') len--; /* The second line contains the info we want */ fgets(linkname, sizeof(linkname), pipe); pclose(pipe); /* Treat special cases: filename between [] and - for login shell */ if (linkname[len] == '-') len++; if (linkname[len] == '[' && linkname[strlen(linkname)] == ']') { len++; linkname[strlen(linkname)] == '\0'; } /* Now, the filename should be in the last column */ _al_sane_strncpy (filename, linkname+len+1, strlen(linkname)-len+1); if (_find_executable_file(filename, output, size)) return; /* Just return the output from ps... */ do_uconvert (filename, U_ASCII, output, U_CURRENT, size); return; } #ifdef ALLEGRO_WITH_MAGIC_MAIN /* Try the captured argv[0] */ if (_find_executable_file(__crt0_argv[0], output, size)) return; #endif /* Give up; return empty string */ do_uconvert ("", U_ASCII, output, U_CURRENT, size); } #endif