示例#1
0
/*
 * Get OS name, version & architecture we're running on
 */
void platform(state *st)
{
#ifdef HAVE_UNAME
#if defined(_AIX) || defined(__linux) || defined(__APPLE__)
	FILE *fp;
#endif
#if defined(__arm__) || defined(__mips__) || defined(__APPLE__)
	char buf[BUFSIZE];
#endif
#ifdef __linux
	struct stat file;
#endif
	struct utsname name;
	char sysname[64];
	char release[64];
	char machine[64];
	char *c;

	/* Fetch system information */
	uname(&name);

	strclear(sysname);
	strclear(release);
	strclear(machine);

	/* AIX-specific */
#ifdef _AIX

	/* Fix uname() results */
	sstrlcpy(machine, "powerpc");
	snprintf(release, sizeof(release), "%s.%s",
		name.version,
		name.release);

	/* Get CPU type */
	if ((fp = popen("/usr/sbin/getsystype -i", "r"))) {
		fgets(machine, sizeof(machine), fp);
		pclose(fp);

		strreplace(machine, ' ', '_');
	}

	/* Get hardware name using shell uname */
	if (!*st->server_description &&
	    (fp = popen("/usr/bin/uname -M", "r"))) {

		fgets(st->server_description,
			sizeof(st->server_description), fp);
		pclose(fp);

		strreplace(st->server_description, ',', ' ');
		chomp(st->server_description);
	}
#endif

	/* Mac OS X, just like Unix but totally different... */
#ifdef __APPLE__

	/* Hardcode OS name */
	sstrlcpy(sysname, "MacOSX");

	/* Get OS X version */
	if ((fp = popen("/usr/bin/sw_vers -productVersion", "r"))) {
		fgets(release, sizeof(release), fp);
		pclose(fp);
	}

	/* Get hardware name */
	if (!*st->server_description &&
	    (fp = popen("/usr/sbin/sysctl -n hw.model", "r"))) {

		/* Read hardware name */
		fgets(buf, sizeof(buf), fp);
		pclose(fp);

		/* Clones are gone now so we'll hardcode the manufacturer */
		sstrlcpy(st->server_description, "Apple ");
		sstrlcat(st->server_description, buf);

		/* Remove hardware revision */
		for (c = st->server_description; *c; c++)
			if (*c >= '0' && *c <= '9') { *c = '\0'; break; }
	}
#endif

	/* Linux uname() just says Linux/2.6 - let's dig deeper... */
#ifdef __linux

	/* Most Linux ARM/MIPS boards have hardware name in /proc/cpuinfo */
#if defined(__arm__) || defined(__mips__)
	if (!*st->server_description && (fp = fopen("/proc/cpuinfo" , "r"))) {

		while (fgets(buf, sizeof(buf), fp)) {
#ifdef __arm__
			if ((c = strkey(buf, "Hardware"))) {
#else
			if ((c = strkey(buf, "machine"))) {
#endif
				sstrlcpy(st->server_description, c);
				chomp(st->server_description);
				break;
			}
		}
		fclose(fp);
	}
#endif

	/* Identify RedHat */
	if (!*sysname && (fp = fopen("/etc/redhat-release", "r"))) {
		fgets(sysname, sizeof(sysname), fp);
		fclose(fp);

		if ((c = strstr(sysname, "release "))) sstrlcpy(release, c + 8);
		if ((c = strchr(release, ' '))) *c = '\0';

		if ((c = strchr(sysname, ' '))) *c = '\0';
		if (strcmp(sysname, "Red") == MATCH) sstrlcpy(sysname, "RedHat");
	}

	/* Identify Slackware */
	if (!*sysname && (fp = fopen("/etc/slackware-version", "r"))) {
		fgets(sysname, sizeof(sysname), fp);
		fclose(fp);

		if ((c = strchr(sysname, ' '))) {
			sstrlcpy(release, c + 1);
			*c = '\0';
		}
	}

	/* Uh-oh.... how about a standard Linux with lsb_release? */
	if (stat("/usr/bin/lsb_release", &file) == OK && (file.st_mode & S_IXOTH)) {

		if (!*sysname && (fp = popen("/usr/bin/lsb_release -i -s", "r"))) {
			fgets(sysname, sizeof(sysname), fp);
			pclose(fp);
		}

		if (!*release && (fp = popen("/usr/bin/lsb_release -r -s", "r"))) {
			fgets(release, sizeof(release), fp);
			pclose(fp);
		}
	}

	/* OK, nothing worked - let's try /etc/issue for sysname */
	if (!*sysname && (fp = fopen("/etc/issue", "r"))) {
		fgets(sysname, sizeof(sysname), fp);
		fclose(fp);

		if ((c = strchr(sysname, ' '))) *c = '\0';
		if ((c = strchr(sysname, '\\'))) *c = '\0';
	}

	/* Debian version should be in /etc/debian_version */
	if (!*release && (fp = fopen("/etc/debian_version", "r"))) {
		fgets (release, sizeof(release), fp);
		fclose(fp);

		if ((c = strchr(release, '/'))) *c = '\0';
	}
#endif

	/* Haiku OS */
#ifdef __HAIKU__

	/* Fix release name */
	snprintf(release, sizeof(release), "R%s", name.release);
#endif

	/* Fill in the blanks using uname() data */
	if (!*sysname) sstrlcpy(sysname, name.sysname);
	if (!*release) sstrlcpy(release, name.release);
	if (!*machine) sstrlcpy(machine, name.machine);

	/* I always liked weird Perl-only functions */
	chomp(sysname);
	chomp(release);
	chomp(machine);

	/* We're only interested in major.minor version */
	if ((c = strchr(release, '.'))) if ((c = strchr(c + 1, '.'))) *c = '\0';
	if ((c = strchr(release, '-'))) *c = '\0';
	if ((c = strchr(release, '/'))) *c = '\0';

	/* Create a nicely formatted platform string */
	snprintf(st->server_platform, sizeof(st->server_platform), "%s/%s %s",
	         sysname,
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
	         machine,
	         release);
#else
	         release,
	         machine);
#endif 

	/* Debug */
	if (st->debug) {
		syslog(LOG_INFO, "generated platform string \"%s\"",
			st->server_platform);
	}

#else
	/* Fallback reply */
	sstrlcpy(st->server_platform, "Unknown computer-like system");
#endif
}


/*
 * Return current CPU load
 */
float loadavg(void)
{
	FILE *fp;
	char buf[BUFSIZE];

	/* Faster Linux version */
#ifdef __linux
	buf[0] = '\0';
	if ((fp = fopen("/proc/loadavg" , "r")) == NULL) return 0;
	fgets(buf, sizeof(buf), fp);
	fclose(fp);

	return (float) atof(buf);

	/* Generic slow version - parse the output of uptime */
#else
#ifdef HAVE_POPEN
	char *c;

	if ((fp = popen("/usr/bin/uptime", "r"))) {
		fgets(buf, sizeof(buf), fp);
		pclose(fp);

		if ((c = strstr(buf, "average: ")) || (c = strstr(buf, "averages: ")))
			return (float) atof(c + 10);
	}
#endif

	/* Fallback reply */
	return 0;
#endif
}
示例#2
0
/*
 * Main
 */
int main(int argc, char *argv[])
{
	struct stat file;
	state st;
	char self[64];
	char selector[BUFSIZE];
	char buf[BUFSIZE];
	char *dest;
	char *c;
#ifdef HAVE_SHMEM
	struct shmid_ds shm_ds;
	shm_state *shm;
	int shmid;
#endif

	/* Get the name of this binary */
	if ((c = strrchr(argv[0], '/'))) sstrlcpy(self, c + 1);
	else sstrlcpy(self, argv[0]);

	/* Initialize state */
#ifdef HAVE_LOCALES
	setlocale(LC_TIME, DATE_LOCALE);
#endif
	init_state(&st);
	srand(time(NULL) / (getpid() + getppid()));

	/* Handle command line arguments */
	parse_args(&st, argc, argv);

	/* Open syslog() */
	if (st.opt_syslog) openlog(self, LOG_PID, LOG_DAEMON);

	/* Make sure the computer is turned on */
#ifdef __HAIKU__
	if (is_computer_on() != TRUE)
		die(&st, ERR_ACCESS, "Please turn on the computer first");
#endif

	/* Refuse to run as root */
#ifdef HAVE_PASSWD
	if (st.opt_root && getuid() == 0)
		die(&st, ERR_ACCESS, "Refusing to run as root");
#endif

	/* Try to get shared memory */
#ifdef HAVE_SHMEM
	if ((shmid = shmget(SHM_KEY, sizeof(shm_state), IPC_CREAT | SHM_MODE)) == ERROR) {

		/* Getting memory failed -> delete the old allocation */
		shmctl(shmid, IPC_RMID, &shm_ds);
		shm = NULL;
	}
	else {
		/* Map shared memory */
		if ((shm = (shm_state *) shmat(shmid, (void *) 0, 0)) == (void *) ERROR)
			shm = NULL;

		/* Initialize mapped shared memory */
		if (shm && shm->start_time == 0) {
			shm->start_time = time(NULL);

			/* Keep server platform & description in shm */
			platform(&st);
			sstrlcpy(shm->server_platform, st.server_platform);
			sstrlcpy(shm->server_description, st.server_description);
		}
	}

	/* For debugging shared memory issues */
	if (!st.opt_shm) shm = NULL;

	/* Get server platform and description */
	if (shm) {
		sstrlcpy(st.server_platform, shm->server_platform);

		if (!*st.server_description)
			sstrlcpy(st.server_description, shm->server_description);
	}
	else
#endif
		platform(&st);

	/* Read selector */
	if (fgets(selector, sizeof(selector) - 1, stdin) == NULL)
		selector[0] = '\0';

	/* Remove trailing CRLF */
	chomp(selector);

	if (st.debug) syslog(LOG_INFO, "client sent us \"%s\"", selector);

	/* Handle hURL: redirect page */
	if (sstrncmp(selector, "URL:") == MATCH) {
		st.req_filetype = TYPE_HTML;
		sstrlcpy(st.req_selector, selector);
		url_redirect(&st);
		return OK;
	}

	/* Handle gopher+ root requests (UMN gopher client is seriously borken) */
	if (sstrncmp(selector, "\t$") == MATCH) {
		printf("+-1" CRLF);
		printf("+INFO: 1Main menu\t\t%s\t%i" CRLF,
			st.server_host,
			st.server_port);
		printf("+VIEWS:" CRLF " application/gopher+-menu: <512b>" CRLF);
		printf("." CRLF);

		if (st.debug) syslog(LOG_INFO, "got a request for gopher+ root menu");
		return OK;
	}

	/* Convert HTTP request to gopher (respond using headerless HTTP/0.9) */
	if (sstrncmp(selector, "GET ") == MATCH ||
	    sstrncmp(selector, "POST ") == MATCH ) {

		if ((c = strchr(selector, ' '))) sstrlcpy(selector, c + 1);
		if ((c = strchr(selector, ' '))) *c = '\0';

		st.req_protocol = PROTO_HTTP;

		if (st.debug) syslog(LOG_INFO, "got HTTP request for \"%s\"", selector);
	}

	/* Save default server_host & fetch session data (including new server_host) */
	sstrlcpy(st.server_host_default, st.server_host);
#ifdef HAVE_SHMEM
	if (shm) get_shm_session(&st, shm);
#endif

	/* Loop through the selector, fix it & separate query_string */
	dest = st.req_selector;
	if (selector[0] != '/') *dest++ = '/';

	for (c = selector; *c;) {

		/* Skip duplicate slashes and /./ */
		while (*c == '/' && *(c + 1) == '/') c++;
		if (*c == '/' && *(c + 1) == '.' && *(c + 2) == '/') c += 2;

		/* Start of a query string (either type 7 or HTTP-style)? */
		if (*c == '\t' || (st.opt_query && *c == '?')) {
			sstrlcpy(st.req_query_string, c + 1);
			if ((c = strchr(st.req_query_string, '\t'))) *c = '\0';
			break;
		}

		/* Start of virtual host hint? */
		if (*c == ';') {
			if (st.opt_vhost) sstrlcpy(st.server_host, c + 1);

			/* Skip vhost on selector */
			while (*c && *c != '\t') c++;
			continue;
		}

		/* Copy valid char */
		*dest++ = *c++;
	}
	*dest = '\0';

	/* Remove encodings from selector */
	strndecode(st.req_selector, st.req_selector, sizeof(st.req_selector));

	/* Deny requests for Slashdot and /../ hackers */
	if (strstr(st.req_selector, "/."))
		die(&st, ERR_ACCESS, "Refusing to serve out dotfiles");

	/* Handle /server-status requests */
#ifdef HAVE_SHMEM
	if (sstrncmp(st.req_selector, SERVER_STATUS) == MATCH) {
		if (shm) server_status(&st, shm, shmid);
		return OK;
	}
#endif

	/* Remove possible extra cruft from server_host */
	if ((c = strchr(st.server_host, '\t'))) *c = '\0';

	/* Guess request filetype so we can die() with style... */
	st.req_filetype = gopher_filetype(&st, st.req_selector, FALSE);

	/* Convert seletor to path & stat() */
	selector_to_path(&st);
	if (st.debug) syslog(LOG_INFO, "path to resource is \"%s\"", st.req_realpath);

	if (stat(st.req_realpath, &file) == ERROR) {

		/* Handle virtual /caps.txt requests */
		if (st.opt_caps && sstrncmp(st.req_selector, CAPS_TXT) == MATCH) {
#ifdef HAVE_SHMEM
			caps_txt(&st, shm);
#else
			caps_txt(&st, NULL);
#endif
			return OK;
		}

		/* Requested file not found - die() */
		die(&st, ERR_NOTFOUND, NULL);
	}

	/* Fetch request filesize from stat() */
	st.req_filesize = file.st_size;

	/* Everyone must have read access but no write access */
	if ((file.st_mode & S_IROTH) == 0)
		die(&st, ERR_ACCESS, "File or directory not world-readable");
	if ((file.st_mode & S_IWOTH) != 0)
		die(&st, ERR_ACCESS, "File or directory world-writeable");

	/* If stat said it was a dir then it's a menu */
	if ((file.st_mode & S_IFMT) == S_IFDIR) st.req_filetype = TYPE_MENU;

	/* Not a dir - let's guess the filetype again... */
	else if ((file.st_mode & S_IFMT) == S_IFREG)
		st.req_filetype = gopher_filetype(&st, st.req_realpath, st.opt_magic);

	/* Menu selectors must end with a slash */
	if (st.req_filetype == TYPE_MENU && strlast(st.req_selector) != '/')
		sstrlcat(st.req_selector, "/");

	/* Change directory to wherever the resource was */
	sstrlcpy(buf, st.req_realpath);

	if ((file.st_mode & S_IFMT) != S_IFDIR) c = dirname(buf);
	else c = buf;

	if (chdir(c) == ERROR) die(&st, ERR_ACCESS, NULL);

	/* Keep count of hits and data transfer */
#ifdef HAVE_SHMEM
	if (shm) {
		shm->hits++;
		shm->kbytes += st.req_filesize / 1024;

		/* Update user session */
		update_shm_session(&st, shm);
	}
#endif

	/* Log the request */
	if (st.opt_syslog) {
		syslog(LOG_INFO, "request for \"gopher://%s:%i/%c%s\" from %s",
			st.server_host,
			st.server_port,
			st.req_filetype,
			st.req_selector,
			st.req_remote_addr);
	}

	/* Check file type & act accordingly */
	switch (file.st_mode & S_IFMT) {
		case S_IFDIR:
			log_combined(&st, HTTP_OK);
			gopher_menu(&st);
			break;

		case S_IFREG:
			log_combined(&st, HTTP_OK);
			gopher_file(&st);
			break;

		default:
			die(&st, ERR_ACCESS, "Refusing to serve out special files");
	}

	/* Clean exit */
	return OK;
}