| Initial commit at 0.16 of geomyidae. - geomyidae - A small C-based gopherd. | |
| git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri… | |
| Log | |
| Files | |
| Refs | |
| Tags | |
| README | |
| LICENSE | |
| --- | |
| commit 97230aa165bee0bffae624707ff98d6098da1823 | |
| Author: Christoph Lohmann <[email protected]> | |
| Date: Fri, 19 Nov 2010 09:20:38 +0100 | |
| Initial commit at 0.16 of geomyidae. | |
| Diffstat: | |
| A LICENSE | 24 ++++++++++++++++++++++++ | |
| A Makefile | 56 +++++++++++++++++++++++++++++… | |
| A README | 43 ++++++++++++++++++++++++++++++ | |
| A arg.h | 19 +++++++++++++++++++ | |
| A geomyidae.8 | 407 +++++++++++++++++++++++++++++… | |
| A handlr.c | 196 +++++++++++++++++++++++++++++… | |
| A handlr.h | 20 ++++++++++++++++++++ | |
| A ind.c | 287 +++++++++++++++++++++++++++++… | |
| A ind.h | 48 +++++++++++++++++++++++++++++… | |
| A index.gph | 6 ++++++ | |
| A main.c | 385 +++++++++++++++++++++++++++++… | |
| A rc.d/Archlinux.conf.d | 4 ++++ | |
| A rc.d/Archlinux.rc.d | 38 +++++++++++++++++++++++++++++… | |
| A rc.d/Gentoo.conf.d | 5 +++++ | |
| A rc.d/Gentoo.init.d | 17 +++++++++++++++++ | |
| A rc.d/NetBSD.rc.d | 55 +++++++++++++++++++++++++++++… | |
| A rc.d/README | 5 +++++ | |
| 17 files changed, 1615 insertions(+), 0 deletions(-) | |
| --- | |
| diff --git a/LICENSE b/LICENSE | |
| @@ -0,0 +1,24 @@ | |
| +MIT/X Consortium License | |
| + | |
| +© 2006-2010 Christoph Lohmann <[email protected]> | |
| +© 2007 Jeff Woodall <[email protected]> | |
| +© 2008 J. A. Neitzel <[email protected]> | |
| +© 2010 James Penketh <[email protected]> | |
| + | |
| +Permission is hereby granted, free of charge, to any person obtaining a | |
| +copy of this software and associated documentation files (the "Software"), | |
| +to deal in the Software without restriction, including without limitation | |
| +the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| +and/or sell copies of the Software, and to permit persons to whom the | |
| +Software is furnished to do so, subject to the following conditions: | |
| + | |
| +The above copyright notice and this permission notice shall be included in | |
| +all copies or substantial portions of the Software. | |
| + | |
| +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| +DEALINGS IN THE SOFTWARE. | |
| diff --git a/Makefile b/Makefile | |
| @@ -0,0 +1,56 @@ | |
| +PROGRAM = geomyidae | |
| +VERSION = 0.16 | |
| + | |
| +PREFIX ?= /usr | |
| +BINDIR ?= $(PREFIX)/bin | |
| +MANDIR ?= $(PREFIX)/man/man8 | |
| + | |
| +#CPPFLAGS += -D_BSD_SOURCE | |
| +CFLAGS += -O2 -Wall -I. -I/usr/include | |
| +LDFLAGS += -L/usr/lib -L. -lc | |
| + | |
| +CFILES = main.c ind.c handlr.c | |
| + | |
| +OBJECTS = ${CFILES:.c=.o} | |
| + | |
| +all: $(PROGRAM) | |
| + | |
| +${PROGRAM}: ${OBJECTS} | |
| + ${CC} ${LDFLAGS} -o ${PROGRAM} ${OBJECTS} | |
| + | |
| +.SUFFIXES : .c .h | |
| + | |
| +.c.o : | |
| + ${CC} ${CFLAGS} ${CPPFLAGS} -c $< | |
| +.c : | |
| + ${CC} ${CFLAGS} ${CPPFLAGS} -c $< | |
| + | |
| +clean : | |
| + @rm -f *.o ${PROGRAM} core *~ | |
| + | |
| +install: $(PROGRAM) | |
| + @mkdir -p ${DESTDIR}${BINDIR} | |
| + @cp -f ${PROGRAM} ${DESTDIR}${BINDIR} | |
| + @strip ${DESTDIR}${BINDIR}/${PROGRAM} | |
| + @chmod 755 ${DESTDIR}${BINDIR}/${PROGRAM} | |
| + @mkdir -p ${DESTDIR}${MANDIR} | |
| + @cp -f geomyidae.8 ${DESTDIR}${MANDIR} | |
| + @chmod 644 ${DESTDIR}${MANDIR}/${PROGRAM}.8 | |
| + | |
| +uninstall: | |
| + @rm -f ${DESTDIR}${BINDIR}/${PROGRAM} | |
| + @rm -f ${DESTDIR}${MANDIR}/${PROGRAM}.8 | |
| + | |
| +dist: clean | |
| + @mkdir -p "${PROGRAM}-${VERSION}" | |
| + @cp -r rc.d README LICENSE index.gph Makefile geomyidae.8 \ | |
| + *.c *.h "${PROGRAM}-${VERSION}" | |
| + @chmod 755 "${PROGRAM}-${VERSION}" | |
| + @chmod 744 "${PROGRAM}-${VERSION}"/* | |
| + @tar -cf "${PROGRAM}-${VERSION}.tar" "${PROGRAM}-${VERSION}" | |
| + @gzip "${PROGRAM}-${VERSION}.tar" | |
| + @mv "${PROGRAM}-${VERSION}.tar.gz" "${PROGRAM}-${VERSION}.tgz" | |
| + @rm -rf "${PROGRAM}-${VERSION}" | |
| + | |
| +.PHONY: all clean dist install uninstall | |
| + | |
| diff --git a/README b/README | |
| @@ -0,0 +1,43 @@ | |
| +A gopherd for Linux/BSD. | |
| + | |
| +Features: | |
| + * gopher menus (see index.gph for an example) | |
| + * dir listings (if no index.gph was found) | |
| + * cgi support (.cgi files are executed) | |
| + * search support in CGI files | |
| + * logging (-l option) and loglevels (-v option) | |
| + | |
| +Usage: | |
| + | |
| + geomyidae [-d] [-l logfile] [-v loglvl] [-b htdocs] [-p port] [-o spor… | |
| + [-u user] [-g group] [-h host] [-i IP] | |
| + -d don't fork into background | |
| + -l logfile setting this will turn on logging into logfi… | |
| + -v loglevel see below (default 7) | |
| + -b htdocs the htdocs root for serving files (default | |
| + /var/gopher) | |
| + -p port set the port where geomyidae should lis… | |
| + (default 70) | |
| + -o sport set the port that should be shown in the dir | |
| + listings | |
| + -u user which user rights the serving children … | |
| + -g group which group rights the serving children should… | |
| + -i IP IP which geomyidae should bind to | |
| + -h host host that should be used in the dir lis… | |
| + | |
| +Loglevels: | |
| + | |
| + 0 - no logging | |
| + 1 - served plain files | |
| + 2 - dir listings | |
| + 4 - HTTP redirects | |
| + 8 - not found queries | |
| + | |
| + 1 + 2 + 4 = 7 (files + dir listings + HTTP) | |
| + | |
| +Init scripts: | |
| + The rc.d directory includes startup scripts for various distributions. | |
| + | |
| + | |
| +Have fun! | |
| + | |
| diff --git a/arg.h b/arg.h | |
| @@ -0,0 +1,19 @@ | |
| +#ifndef ARG_H | |
| +#define ARG_H | |
| + | |
| +#define USED(x) ((void)(x)) | |
| + | |
| +extern char *argv0; | |
| + | |
| +#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\ | |
| + argv[0] && argv[0][0]=='-' && argv[0][1];\ | |
| + argc--, argv++) {\ | |
| + char _argc;\ | |
| + _argc = argv[0][1];\ | |
| + switch(_argc) | |
| +#define ARGEND USED(_argc);} USED(argv);USED(argc); | |
| +#define EARGF(x) ((argv[1] == nil)? ((x), abort(), (char *)0) :\ | |
| + (argc--, argv++, argv[0])) | |
| + | |
| +#endif | |
| + | |
| diff --git a/geomyidae.8 b/geomyidae.8 | |
| @@ -0,0 +1,407 @@ | |
| +.\" geomyidae.8 handcrafted in GNU groff -mdoc using nvi | |
| +.\" | |
| +.Dd March 9, 2008 | |
| +.Dt GEOMYIDAE 8 | |
| +.Os | |
| +. | |
| +.Sh NAME | |
| +.Nm geomyidae | |
| +.Nd a gopher daemon for Linux/BSD | |
| +. | |
| +.Sh SYNOPSIS | |
| +.Nm | |
| +.Bk -words | |
| +.Op Fl d | |
| +.Op Fl l Ar logfile | |
| +.Op Fl v Ar loglevel | |
| +.Op Fl b Ar base | |
| +.Op Fl p Ar port | |
| +.Op Fl o Ar sport | |
| +.Op Fl u Ar user | |
| +.Op Fl g Ar group | |
| +.Op Fl h Ar host | |
| +.Op Fl i Ar IP | |
| +.Ek | |
| +. | |
| +.Sh DESCRIPTION | |
| +.Nm | |
| +is a daemon for serving the protocol specified in | |
| +.Em RFC 1436 | |
| +(Gopher). Under 1000 lines of C by design, it is lightweight yet supports | |
| +dynamic content, automatic file/directory indexing, logging and privilege | |
| +separation. | |
| +. | |
| +.Sh IMPLEMENTATION | |
| +Installation is straightforward: grab the zipped tar file, expand it in | |
| +an appropriate temp directory, change to the | |
| +.Qq "../geomyidae-x.xx" | |
| +directory, tweak the Makefile if desired (installs in | |
| +.Qq "/usr/bin" | |
| +by default), then run the | |
| +.Sq "make ; make install" | |
| +commands. The resulting executable should be run by root. | |
| +. | |
| +.Ss Installation example: | |
| +.Pp | |
| +.Bd -literal | |
| + % wget http://www.r-36.net/src/geomyidae/geomyidae-current.tgz; | |
| + % tar -xzvf geomyidae-*.tgz; | |
| + % cd geomyidae-*; | |
| + % make; sudo make install; | |
| + % sudo mkdir -p /var/gopher; | |
| + % sudo cp index.gph /var/gopher; | |
| + % sudo eomyidae -l /var/log/geomyidae.log -b /var/gopher -p 70; | |
| + % tail -f /var/log/geomyidae.log; | |
| + # | |
| + # Use whatever gopher client you like to gopher to | |
| + # gopher://localhost | |
| + # | |
| + # Have fun! | |
| +.Ed | |
| +. | |
| +.Ss Running | |
| +Geomyidae should normally be started by root. Though, it can be started | |
| +by a regular user provided that the base directory and its contents are owned | |
| +by the same user. Geomyidae will only serve content within the base directory | |
| +tree and will drop privileges to the | |
| +.Fl u Ar user | |
| +and | |
| +.Fl g Ar group | |
| +values if set. See | |
| +.Ic OPTIONS | |
| +below for specifics. | |
| +. | |
| +.Sh OPTIONS | |
| +geomyidae options and default settings: | |
| +.Pp | |
| +.Bl -tag -width ".Fl test Ao Ar string Ac" | |
| +. | |
| +.It Fl d | |
| +Don't fork into background. | |
| +. | |
| +.It Fl l Ar logfile | |
| +Specify the file where the log output will be written (default: no default). | |
| +. | |
| +.It Fl v Ar loglevel | |
| +Set the logging level (default: 15). | |
| +. | |
| +.Bd -literal | |
| +Loglevels: | |
| + 0 - no logging | |
| + 1 - served plain files | |
| + 2 - directory listings | |
| + 4 - HTTP redirects | |
| + 8 - errors (e.g., not found) | |
| + e.g.: | |
| + 1 + 2 + 4 + 8 = 15 | |
| + (files + directories + HTTP + errors) | |
| +.Ed | |
| +. | |
| +.It Fl b Ar base | |
| +Root directory to serve (default: /var/gopher) | |
| +. | |
| +.It Fl p Ar port | |
| +Port geomyidae should listen on (default: 70) | |
| +. | |
| +.It Fl o Ar sport | |
| +Port geomyidae displays within base directory (default: 70). | |
| +. | |
| +Use in conjunction with | |
| +.Ic -p | |
| +for obfuscating actual port neomyidae is running on. | |
| +. | |
| +.It Fl u Ar user | |
| +Sets the user to which privileges drop when geomyidae is ready | |
| +to accept network connections (default: user geomyidae runs as). | |
| +Helps improve security by reducing privileges during request | |
| +processing. | |
| +. | |
| +.It Fl g Ar group | |
| +Sets the group to which privileges drop when geomyidae is ready | |
| +to accept network connections (default: group geomyidae runs as). | |
| +Helps improve security by reducing privileges during request | |
| +processing. | |
| +. | |
| +.It Fl h Ar host | |
| +Host to use in directory listings (default: localhost) | |
| +. | |
| +.It Fl i Ar IP | |
| +IP to which geomyidae binds to (default: 127.0.0.1) | |
| +.El | |
| +. | |
| +.Sh FORMATTING | |
| +Structured Gopher space(s) can be created with geomyidae through the | |
| +use of special indexing files of the form | |
| +.Ic <name>.gph | |
| +which, if present, geomyidae uses to format and/or filter the contents of | |
| +the base directory (/var/gopher by default) and create gopher menus. | |
| +However, index files are | |
| +.Em not | |
| +required: if no .gph files are found geomyidae simply lists the directory | |
| +contents in alphanumeric order. In addition, a directory can utilize | |
| +multiple index files to create a layered gopher environment without the | |
| +use of sub-directories: ie. pictures.gph, music.gph, documents.gph could | |
| +be "directories" within main.gph, yet all reside in /var/gopher along with | |
| +their respective files (*.jpg, *.mp3, *.pdf for example). | |
| +. | |
| +.Ss Anatomy of an index.gph file | |
| +In general, each line of an index.gph file has the following structure: | |
| +.Pp | |
| +.Bl -inset -offset indent | |
| +.It Ic [<type>|<desc>|<path>|<port>] | |
| +.El | |
| +.Pp | |
| +where, | |
| +.Bl -inset -offset indent | |
| +.It Ic <type> | |
| += A valid gopher Item Type. | |
| +.Pp | |
| +Some common Gopher Types as defined in | |
| +.Em RFC 1436 | |
| +: | |
| +. | |
| +.Bd -literal | |
| + 0 Item is a file | |
| + 1 Gopher directory | |
| + 3 Error | |
| + 7 Item is an Index-Search server. | |
| + 8 Item points to a text-based telnet session. | |
| + 9 Binary file. Client reads until TCP connection closes! | |
| + g GIF format graphics file. | |
| + I Indeterminate image file. Client decides how to display. | |
| +.Ed | |
| +.Pp | |
| +In addition, geomyidae provides these: | |
| +.Bd -literal | |
| + h Item is a hypertext (HTTP) link | |
| + i Informational Item (used for descriptive purposes) | |
| +.Ed | |
| +. | |
| +.Pp | |
| +Note: geomyidae doesn't require "informational" text to be formally | |
| +Typed as "[i|...]"; any line | |
| +.Em not | |
| +beginning with "[" is treated as informational, greatly simplifying the | |
| +formatting of index.gph files. | |
| +If a line begins with "t", this "t" is left out. This measurement is | |
| +there to allow lines "informational" text beginning with "[". | |
| +. | |
| +.It Ic <desc> | |
| += description of gopher item. Most printable characters should work. | |
| +. | |
| +.It Ic <path> | |
| += full path to gopher item (base value is / ). Use the "Err" path for | |
| +items not intended to be served. | |
| +. | |
| +.It Ic <host> | |
| += hostname or IP hosting the gopher item. Must be resolvable for the | |
| +intended clients. | |
| +. | |
| +.It Ic <port> | |
| += TCP port number ( usually 70) | |
| +. | |
| +May be omitted if defaults are used. | |
| +.El | |
| +. | |
| +.Ss index.gph Example | |
| +A root.gph file for a server running on host=frog.bog, port=70. Note use | |
| +of optional [i]nformational Item (line 2) for vertical space insertion: | |
| +.Pp | |
| +.Bd -literal -offset indent | |
| +Welcome to Frog.bog | |
| +[i||||] | |
| +[0|About this server|about.txt|frog.bog|70] | |
| +[0|Daily Log|/dtail.cgi|frog.bog|70] | |
| +[1|Phlog: like a blog, but not|/PHLOG|frog.bog|70] | |
| +[9|Some binary file|widget.exe|frog.bog|70] | |
| +[I|Snowflake picture|snowflake.jpg|frog.bog|70] | |
| + | |
| +Links and Searches | |
| +[1|Go to R-36.net|/|gopher.r-36.net|70] | |
| +[h|Go to NetBSD.org|URL:http://netbsd.org|frog.bog|70] | |
| +[7|Query US Weather by Zipcode|/weather.cgi?|frog.bog|70] | |
| +[7|Search Veronica II|/v2/vs|gopher.floodgap.com|70] | |
| +[8|Telnet to SDF Public Access Unix System||freeshell.org|23] | |
| +.Ed | |
| +. | |
| +.Pp | |
| +The above looks something like this in a text-based gopher client: | |
| +.Pp | |
| +.Bl -tag -width ".It Ic WIDTHS" -compact -offset indent | |
| +.D1 Welcome to Frog.bog | |
| +.Pp | |
| +.It Ic (FILE) | |
| +About this server | |
| +.It Ic (FILE) | |
| +Daily Log | |
| +.It Ic (DIR) | |
| +Phlog: like a blog, but not | |
| +.It Ic (BIN) | |
| +Some binary file | |
| +.It Ic (IMG) | |
| +Snowflake picture | |
| +.El | |
| +.Pp | |
| +.Bl -tag -width ".It Ic WIDTHS" -compact -offset indent | |
| +.D1 Links and Searches | |
| +.It Ic (DIR) | |
| +Go to R-36.net | |
| +.It Ic (HTML) | |
| +Go to NetBSD.org | |
| +.It Ic (?) | |
| +Query US Weather by Zipcode | |
| +.It Ic (?) | |
| +Search Veronica II | |
| +.It Ic (TEL) | |
| +Telnet to SDF Public Access Unix System | |
| +.El | |
| +.Pp | |
| +.Sh USING DYNAMIC CONTENT | |
| +Dynamic content can be generated under geomyidae by simply creating a file | |
| +in for form of | |
| +.Ic <name>.cgi | |
| +in a directory that is being served. Such files are run as a shell script. | |
| +(See below for description.) | |
| +.Pp | |
| +ex. dtail.cgi - prints daily log entries | |
| +. | |
| +.Bd -literal -offset indent | |
| +#!/bin/sh -e | |
| +echo "Logged activity for `date '+%A, %B %d, %Y'` :" | |
| +echo "===============================================" | |
| +LOG="/var/log/gopherd.log" | |
| +DATE=`date "+%a %b %d"` | |
| +/usr/bin/grep "$DATE" $LOG | |
| +exit 0 | |
| +.Ed | |
| +. | |
| +.Pp | |
| +Geomyidae supports two variable queries. The basic form is | |
| +.Pp | |
| +.D1 executable.cgi{argv[1]}?{argv[2]} | |
| +.Pp | |
| +where | |
| +.Pp | |
| +.D1 argv[1] = strings (everything before the '?' in the query) | |
| +.D1 argv[2] = arguments (everything behind the '?' in the query) | |
| +.Pp | |
| +A search query request must have an item Type of "7" to be called | |
| +from an index.gph file. It may also need a "?" suffix in the <path> | |
| +field. | |
| +. | |
| +.Pp | |
| +ex. hello.cgi - say hello to user | |
| +. | |
| +.Bd -literal -offset indent | |
| +#!/bin/sh | |
| +NAME=$1 | |
| +echo "" | |
| +echo Hello $NAME - welcome to Frog.bog | |
| +exit 0 | |
| +.Ed | |
| +. | |
| +.Pp | |
| +Call the above with the following index.gph entry: | |
| +.Pp | |
| +.D1 [7|Hello You - Please enter your name|/hello.cgi?|frog.bog|70] | |
| +. | |
| +.Pp | |
| +And do a simple | |
| +.Xr snarf 1 | |
| +query: | |
| +.Pp | |
| +.D1 % snarf Qo gopher://frog.bog/7/hello.cgi?Christoph Qc - | |
| +.D1 Hello Christoph - welcome to Frog.bog | |
| +.Dl % | |
| +. | |
| +.Sh LOG FILES | |
| +.Pp | |
| +The log file (/var/log/gopherd.log is default) has the following structure: | |
| +. | |
| +.Pp | |
| +.Ic [<date>|<IP:port>] <item path> <query> (<status>) | |
| +. | |
| +.Pp | |
| +where, | |
| +. | |
| +.Bl -inset | |
| +.It Ic <date> | |
| += access date and time (std 'date' format) | |
| +.Bl -inset -offset indent | |
| +ex. | |
| +.Qq "Sun Feb 17 06:11:10 PST 2008" | |
| +.El | |
| +.It Ic <IP:port> | |
| += client IP address and port served | |
| +.Bl -inset -offset indent | |
| +ex. | |
| +.Qq "24.208.18.127:16857" | |
| +.El | |
| +.Pp | |
| +.It Ic <item path> | |
| += full path to item served | |
| +.Bl -inset -offset indent | |
| +ex. | |
| +.D1 Qo "/PICS/simple2.jpg" Qc for an image file | |
| +.D1 Qo "/PICS" Qc for a directory access | |
| +.El | |
| +.It Ic <query> | |
| += query term submitted (Type 7 requests only) | |
| +.Bl -inset -offset indent | |
| +ex. | |
| +.Dl % snarf Qq "gopher://frog.bog/7/hello.cgi?Christoph" | |
| +.Dl would log Qo "Christoph" Qc as the query term. | |
| +.El | |
| +.It Ic (<status>) | |
| += status of client request | |
| +.Bl -inset -offset indent | |
| +ex. - some common status entries: | |
| +.El | |
| +.Pp | |
| +.Bl -hang -width XXXXXXXXXXXXXXXX -compact -offset XXXXXXXXXXXX | |
| +.It Qo (serving) Qc | |
| +=> a successful request | |
| +.It Qo (not found) Qc | |
| +=> an unsuccessful request | |
| +.It Qo (HTTP redirect) Qc | |
| +=> web link redirect (Type h) | |
| +.It Qo (dir listing) Qc | |
| +=> unindexed directory listing | |
| +.El | |
| +.El | |
| +. | |
| +.Sh FILES | |
| +README, LICENSE, index.gph | |
| +. | |
| +.Sh "SEE ALSO" | |
| +Links for further information on gopher: | |
| +.Pp | |
| +.D1 Pa gopher://gopher.gopherproject.org | |
| +.D1 Pa http://www.gopherproject.org | |
| +.Pp | |
| +.Sh STANDARDS | |
| +.Em Internet RFC 1436 | |
| +. | |
| +.Sh HISTORY | |
| +Geomyidae started as a Linux/BSD port of the Plan 9 gopherd_P9 server. | |
| +Originally called gopherd_BSD, the name was later changed to Geomyidae | |
| +(latin), the taxonomic family of burrowing rodents known as "pocket | |
| +gophers" which are in fact the true gophers. Because of inconsitencies | |
| +and the UNIX culture, the name was changed to lowercase in 2010. | |
| +. | |
| +.Sh AUTHORS | |
| +See LICENSE file for authors in the distribution. | |
| +. | |
| +.Sh LICENSE | |
| +Geomyidae is released under the MIT/X Consortium License. | |
| +. | |
| +.Sh BUGS | |
| +Geomyidae occasionally aborts silently if too many simultaneous | |
| +requests are made. Limiting gopher traffic via firewall rules | |
| +may help. | |
| +.Pp | |
| +Dynamic content functionality may vary across gopher clients. | |
| +. | |
| +.Ss "Reporting Bugs" | |
| +Report bugs to: | |
| +.An "Christoph Lohmann" Aq [email protected] | |
| diff --git a/handlr.c b/handlr.c | |
| @@ -0,0 +1,196 @@ | |
| +/* | |
| + * Copy me if you can. | |
| + * by 20h | |
| + */ | |
| + | |
| +#include <unistd.h> | |
| +#include <memory.h> | |
| +#include <netdb.h> | |
| +#include <netinet/in.h> | |
| +#include <fcntl.h> | |
| +#include <stdio.h> | |
| +#include <stdlib.h> | |
| +#include <string.h> | |
| +#include <sys/socket.h> | |
| +#include <sys/types.h> | |
| +#include <sys/stat.h> | |
| +#include <dirent.h> | |
| +#include "ind.h" | |
| +#include "arg.h" | |
| + | |
| +void | |
| +handledir(int sock, char *path, char *port, char *base, char *args, | |
| + char *sear) | |
| +{ | |
| + char *pa, *file, *e, *addr, *par, *b; | |
| + struct dirent **dirent; | |
| + int ndir, i; | |
| + struct stat st; | |
| + filetype *type; | |
| + | |
| + USED(sear); | |
| + addr = nil; | |
| + | |
| + pa = gstrdup(path); | |
| + e = strrchr(pa, '/'); | |
| + if(e != nil) { | |
| + *e = '\0'; | |
| + | |
| + if(args == nil) { | |
| + addr = gmallocz(512, 2); | |
| + if(gethostname(addr, 512) == -1) { | |
| + perror("gethostname"); | |
| + close(sock); | |
| + free(addr); | |
| + free(pa); | |
| + return; | |
| + } | |
| + } else | |
| + addr = gstrdup(args); | |
| + | |
| + par = gstrdup(pa); | |
| + b = strrchr(par + strlen(base), '/'); | |
| + if(b != nil) { | |
| + *b = '\0'; | |
| + tprintf(sock, "1..\t%s\t%s\t%s\r\n", | |
| + par + strlen(base), addr, port); | |
| + } | |
| + free(par); | |
| + | |
| + ndir = scandir(pa, &dirent, 0, alphasort); | |
| + if(ndir < 0) { | |
| + perror("scandir"); | |
| + close(sock); | |
| + free(addr); | |
| + free(pa); | |
| + return; | |
| + } else { | |
| + for(i = 0; i < ndir; i++) { | |
| + if(dirent[i]->d_name[0] == '.') { | |
| + free(dirent[i]); | |
| + continue; | |
| + } | |
| + | |
| + type = gettype(dirent[i]->d_name); | |
| + file = smprintf("%s/%s", pa, | |
| + dirent[i]->d_name); | |
| + if(stat(file, &st) >= 0 && S_ISDIR(st.st_mode)) | |
| + type = gettype("index.gph"); | |
| + e = file + strlen(base); | |
| + tprintf(sock, "%c%s\t%s\t%s\t%s\r\n", *type->t… | |
| + dirent[i]->d_name, e, addr, port); | |
| + free(file); | |
| + free(dirent[i]); | |
| + } | |
| + free(dirent); | |
| + } | |
| + tprintf(sock, "\r\n"); | |
| + } | |
| + | |
| + if(addr != nil) | |
| + free(addr); | |
| + free(pa); | |
| + close(sock); | |
| + return; | |
| +} | |
| + | |
| +void | |
| +handlegph(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear) | |
| +{ | |
| + Indexs *act; | |
| + int i; | |
| + char addr[512]; | |
| + | |
| + USED(base); | |
| + USED(args); | |
| + USED(sear); | |
| + | |
| + act = scanfile(file); | |
| + if(act != nil) { | |
| + if(args == nil) { | |
| + if(gethostname(addr, sizeof(addr)) == -1) { | |
| + perror("gethostname"); | |
| + close(sock); | |
| + return; | |
| + } | |
| + } else | |
| + snprintf(addr, sizeof(addr), "%s", args); | |
| + | |
| + | |
| + for(i = 0; i < act->num; i++) { | |
| + if(!strncmp(act->n[i]->e[3], "server", 6)) { | |
| + free(act->n[i]->e[3]); | |
| + act->n[i]->e[3] = gstrdup(addr); | |
| + } | |
| + if(!strncmp(act->n[i]->e[4], "port", 4)) { | |
| + free(act->n[i]->e[4]); | |
| + act->n[i]->e[4] = gstrdup(port); | |
| + } | |
| + tprintf(sock, "%.1s%s\t%s\t%s\t%s\r\n", | |
| + act->n[i]->e[0], act->n[i]->e[1], | |
| + act->n[i]->e[2], act->n[i]->e[3], | |
| + act->n[i]->e[4]); | |
| + | |
| + freeelem(act->n[i]); | |
| + act->n[i] = nil; | |
| + } | |
| + tprintf(sock, "\r\n.\r\n\r\n"); | |
| + | |
| + freeindex(act); | |
| + } | |
| + | |
| + close(sock); | |
| + return; | |
| +} | |
| + | |
| +void | |
| +handlebin(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear) | |
| +{ | |
| + char sendb[1024]; | |
| + int len, fd; | |
| + | |
| + len = -1; | |
| + USED(port); | |
| + USED(base); | |
| + USED(args); | |
| + USED(sear); | |
| + | |
| + fd = open(file, O_RDONLY); | |
| + if(fd >= 0) { | |
| + while((len = read(fd, sendb, sizeof(sendb))) > 0) | |
| + send(sock, sendb, len, 0); | |
| + close(fd); | |
| + } | |
| + | |
| + close(sock); | |
| + return; | |
| +} | |
| + | |
| +void | |
| +handlecgi(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear) | |
| +{ | |
| + char *p; | |
| + | |
| + USED(port); | |
| + USED(base); | |
| + | |
| + p = strrchr(file, '/'); | |
| + if(p == nil) | |
| + p = file; | |
| + | |
| + dup2(sock, 1); | |
| + dup2(sock, 0); | |
| + dup2(sock, 2); | |
| + | |
| + if(sear == nil) | |
| + sear = ""; | |
| + | |
| + execl(file, p, sear, args, nil); | |
| + | |
| + close(sock); | |
| + return; | |
| +} | |
| + | |
| diff --git a/handlr.h b/handlr.h | |
| @@ -0,0 +1,20 @@ | |
| +/* | |
| + * Copy me if you can. | |
| + * by 20h | |
| + */ | |
| + | |
| +#ifndef HANDLR_H | |
| +#define HANDLR_H | |
| + | |
| +#define nil NULL | |
| + | |
| +void handledir(int sock, char *path, char *port, char *base, char *args, | |
| + char *sear); | |
| +void handlegph(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear); | |
| +void handlebin(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear); | |
| +void handlecgi(int sock, char *file, char *port, char *base, char *args, | |
| + char *sear); | |
| + | |
| +#endif | |
| diff --git a/ind.c b/ind.c | |
| @@ -0,0 +1,287 @@ | |
| +/* | |
| + * Copy me if you can. | |
| + * by 20h | |
| + */ | |
| + | |
| +#include <unistd.h> | |
| +#include <stdarg.h> | |
| +#include <string.h> | |
| +#include <memory.h> | |
| +#include <fcntl.h> | |
| +#include <stdio.h> | |
| +#include <stdlib.h> | |
| +#include "ind.h" | |
| +#include "handlr.h" | |
| + | |
| +filetype type[] = { | |
| + {"default", "0", handlebin}, | |
| + {"gph", "1", handlegph}, | |
| + {"cgi", "0", handlecgi}, | |
| + {"bin", "9", handlebin}, | |
| + {"tgz", "9", handlebin}, | |
| + {"gz", "9", handlebin}, | |
| + {"jpg", "I", handlebin}, | |
| + {"gif", "g", handlebin}, | |
| + {"png", "I", handlebin}, | |
| + {"bmp", "I", handlebin}, | |
| + {"txt", "0", handlebin}, | |
| + {"html", "0", handlebin}, | |
| + {"htm", "0", handlebin}, | |
| + {"xhtml", "0", handlebin}, | |
| + {"css", "0", handlebin}, | |
| + {nil, nil, nil}, | |
| +}; | |
| + | |
| +filetype * | |
| +gettype(char *filename) | |
| +{ | |
| + char *end; | |
| + int i; | |
| + | |
| + end = strrchr(filename, '.'); | |
| + if(end == nil) | |
| + return &type[0]; | |
| + end++; | |
| + | |
| + for(i = 0; type[i].end != nil; i++) | |
| + if(!strncasecmp(end, type[i].end, strlen(type[i].end))) | |
| + return &type[i]; | |
| + | |
| + return &type[0]; | |
| +} | |
| + | |
| +void * | |
| +gmallocz(int l, int d) | |
| +{ | |
| + char *ret; | |
| + | |
| + ret = malloc(l); | |
| + if(ret == nil) { | |
| + perror("malloc"); | |
| + exit(1); | |
| + } | |
| + | |
| + if(d) | |
| + memset(ret, 0, l); | |
| + | |
| + return (void *)ret; | |
| +} | |
| + | |
| +char * | |
| +gstrdup(char *str) | |
| +{ | |
| + char *ret; | |
| + | |
| + ret = strdup(str); | |
| + if(ret == nil) { | |
| + perror("strdup"); | |
| + exit(1); | |
| + } | |
| + | |
| + return ret; | |
| +} | |
| + | |
| +char * | |
| +readln(int fd) | |
| +{ | |
| + char *ret; | |
| + int len; | |
| + | |
| + len = 1; | |
| + | |
| + ret = malloc(2); | |
| + while(read(fd, &ret[len - 1], 1) > 0 && ret[len - 1] != '\n') | |
| + ret = realloc(ret, ++len + 1); | |
| + if(ret[len - 1] != '\n') { | |
| + free(ret); | |
| + return nil; | |
| + } | |
| + ret[len - 1] = '\0'; | |
| + | |
| + return ret; | |
| +} | |
| + | |
| +void | |
| +freeelem(Elems *e) | |
| +{ | |
| + | |
| + if(e != nil) { | |
| + if(e->e != nil) { | |
| + for(;e->num > 0; e->num--) | |
| + if(e->e[e->num - 1] != nil) | |
| + free(e->e[e->num - 1]); | |
| + free(e->e); | |
| + } | |
| + free(e); | |
| + } | |
| + return; | |
| +} | |
| + | |
| +void | |
| +freeindex(Indexs *i) | |
| +{ | |
| + | |
| + if(i != nil) { | |
| + if(i->n != nil) { | |
| + for(;i->num > 0; i->num--) | |
| + if(i->n[i->num - 1] != nil) | |
| + freeelem(i->n[i->num - 1]); | |
| + free(i->n); | |
| + } | |
| + free(i); | |
| + } | |
| + | |
| + return; | |
| +} | |
| + | |
| +void | |
| +addelem(Elems *e, char *s) | |
| +{ | |
| + | |
| + e->num++; | |
| + e->e = realloc(e->e, sizeof(char *) * e->num); | |
| + e->e[e->num - 1] = gmallocz(strlen(s) + 1, 0); | |
| + strcpy(e->e[e->num - 1], s); | |
| + | |
| + return; | |
| +} | |
| + | |
| +Elems * | |
| +getadv(char *str) | |
| +{ | |
| + char *b, *e; | |
| + Elems *ret; | |
| + | |
| + ret = gmallocz(sizeof(Elems), 2); | |
| + if(*str != '[') { | |
| + b = str; | |
| + if(*str == 't') | |
| + b++; | |
| + addelem(ret, "i"); | |
| + addelem(ret, b); | |
| + addelem(ret, "Err"); | |
| + addelem(ret, "server"); | |
| + addelem(ret, "port"); | |
| + | |
| + return ret; | |
| + } | |
| + | |
| + b = str + 1; | |
| + while((e = strchr(b, '|')) != nil) { | |
| + *e = '\0'; | |
| + e++; | |
| + addelem(ret, b); | |
| + b = e; | |
| + } | |
| + | |
| + e = strchr(b, ']'); | |
| + if(e != nil) { | |
| + *e = '\0'; | |
| + addelem(ret, b); | |
| + } | |
| + if(ret->e == nil) { | |
| + free(ret); | |
| + return nil; | |
| + } | |
| + | |
| + return ret; | |
| +} | |
| + | |
| +Indexs * | |
| +scanfile(char *fname) | |
| +{ | |
| + char *ln; | |
| + int fd; | |
| + Indexs *ret; | |
| + Elems *el; | |
| + | |
| + fd = open(fname, O_RDONLY); | |
| + if(fd < 0) | |
| + return nil; | |
| + | |
| + ret = gmallocz(sizeof(Indexs), 2); | |
| + | |
| + while((ln = readln(fd)) != nil) { | |
| + el = getadv(ln); | |
| + free(ln); | |
| + if(el == nil) | |
| + continue; | |
| + | |
| + ret->num++; | |
| + ret->n = realloc(ret->n, sizeof(Elems) * ret->num); | |
| + ret->n[ret->num - 1] = el; | |
| + el = nil; | |
| + } | |
| + close(fd); | |
| + | |
| + if(ret->n == nil) { | |
| + free(ret); | |
| + return nil; | |
| + } | |
| + | |
| + return ret; | |
| +} | |
| + | |
| +void | |
| +tprintf(int fd, char *fmt, ...) | |
| +{ | |
| + va_list fmtargs; | |
| + int fd2; | |
| + FILE *fp; | |
| + | |
| + fd2 = dup(fd); | |
| + fp = fdopen(fd2, "w"); | |
| + if(fp == nil) { | |
| + perror("fdopen"); | |
| + return; | |
| + } | |
| + | |
| + va_start(fmtargs, fmt); | |
| + vfprintf(fp, fmt, fmtargs); | |
| + va_end(fmtargs); | |
| + | |
| + fclose(fp); | |
| + | |
| + return; | |
| +} | |
| + | |
| +int | |
| +initlogging(char *logf) | |
| +{ | |
| + int fd; | |
| + | |
| + fd = open(logf, O_APPEND | O_WRONLY | O_CREAT, 0644); | |
| + | |
| + return fd; | |
| +} | |
| + | |
| +void | |
| +stoplogging(int fd) | |
| +{ | |
| + | |
| + close(fd); | |
| + | |
| + return; | |
| +} | |
| + | |
| +char * | |
| +smprintf(char *fmt, ...) | |
| +{ | |
| + va_list fmtargs; | |
| + char *ret; | |
| + int size; | |
| + | |
| + ret = ""; | |
| + | |
| + va_start(fmtargs, fmt); | |
| + size = vsnprintf(ret, 0, fmt, fmtargs); | |
| + va_end(fmtargs); | |
| + | |
| + ret = gmallocz(++size, 2); | |
| + va_start(fmtargs, fmt); | |
| + vsnprintf(ret, size, fmt, fmtargs); | |
| + va_end(fmtargs); | |
| + | |
| + return ret; | |
| +} | |
| + | |
| diff --git a/ind.h b/ind.h | |
| @@ -0,0 +1,48 @@ | |
| +/* | |
| + * Copy me if you can. | |
| + * by 20h | |
| + */ | |
| + | |
| +#ifndef IND_H | |
| +#define IND_H | |
| + | |
| +#include <stdio.h> | |
| +#define nil NULL | |
| + | |
| +extern int glfd; | |
| + | |
| +typedef struct Elems Elems; | |
| +struct Elems { | |
| + char **e; | |
| + int num; | |
| +}; | |
| + | |
| +typedef struct Indexs Indexs; | |
| +struct Indexs { | |
| + Elems **n; | |
| + int num; | |
| +}; | |
| + | |
| +typedef struct filetype filetype; | |
| +struct filetype { | |
| + char *end; | |
| + char *type; | |
| + void (* f)(int, char *, char *, char *, char *, char *); | |
| +}; | |
| + | |
| +filetype *gettype(char *filename); | |
| +void *gmallocz(int l, int d); | |
| +char *gstrdup(char *str); | |
| +Indexs *scanfile(char *fname); | |
| +Elems *getadv(char *str); | |
| +void addelem(Elems *e, char *s); | |
| +void freeindex(Indexs *i); | |
| +void freeelem(Elems *e); | |
| +char *readln(int fd); | |
| +void tprintf(int fd, char *fmt, ...); | |
| +int initlogging(char *logf); | |
| +void stoplogging(int fd); | |
| +char *smprintf(char *fmt, ...); | |
| + | |
| +#endif | |
| + | |
| diff --git a/index.gph b/index.gph | |
| @@ -0,0 +1,6 @@ | |
| +comment | |
| +tcomment | |
| +[1|R-36|/|server|port] | |
| +[0|file - comment|/file.dat|server|port] | |
| +[h|http://www.heise.de|URL:http://www.heise.de|server|port] | |
| + | |
| diff --git a/main.c b/main.c | |
| @@ -0,0 +1,385 @@ | |
| +/* | |
| + * Copy me if you can. | |
| + * by 20h | |
| + */ | |
| + | |
| +#include <unistd.h> | |
| +#include <dirent.h> | |
| +#include <memory.h> | |
| +#include <netdb.h> | |
| +#include <netinet/in.h> | |
| +#include <fcntl.h> | |
| +#include <stdio.h> | |
| +#include <stdlib.h> | |
| +#include <sys/socket.h> | |
| +#include <sys/stat.h> | |
| +#include <sys/wait.h> | |
| +#include <sys/types.h> | |
| +#include <signal.h> | |
| +#include <time.h> | |
| +#include <pwd.h> | |
| +#include <grp.h> | |
| +#include "ind.h" | |
| +#include "handlr.h" | |
| +#include "arg.h" | |
| + | |
| +enum { | |
| + NOLOG = 0, | |
| + FILES = 1, | |
| + DIRS = 2, | |
| + HTTP = 4, | |
| + ERRORS = 8, | |
| +}; | |
| + | |
| +int glfd = -1; | |
| +int loglvl = 15; | |
| + | |
| +char *argv0; | |
| +char *stdbase = "/var/gopher"; | |
| +char *stdport = "70"; | |
| +char *indexf = "/index.gph"; | |
| +char *err = "0Sorry, but the requested token could not be found\tErr" | |
| + "\tlocalhost\t70\r\n.\r\n\r\n"; | |
| +char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//E… | |
| + " \"DTD/xhtml-transitional.dtd\">\n" | |
| + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n" | |
| + " <head>\n" | |
| + " <title>gopher redirect</title>\n" | |
| + "\n" | |
| + " <meta http-equiv=\"Refresh\" content=\"1;url=%s\" />\n" | |
| + " </head>\n" | |
| + " <body>\n" | |
| + " This page is for redirecting you to: <a href=\"%s\">%s</a… | |
| + " </body>\n" | |
| + "</html>\n"; | |
| + | |
| +int | |
| +dropprivileges(struct group *gr, struct passwd *pw) | |
| +{ | |
| + | |
| + if(gr != nil) | |
| + if(setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0) | |
| + return -1; | |
| + if(pw != nil) { | |
| + if(gr == nil) { | |
| + if(setgroups(1, &pw->pw_gid) != 0 || | |
| + setgid(pw->pw_gid) != 0) | |
| + return -1; | |
| + } | |
| + if(setuid(pw->pw_uid) != 0) | |
| + return -1; | |
| + } | |
| + | |
| + return 0; | |
| +} | |
| + | |
| +char * | |
| +securepath(char *p, int len) | |
| +{ | |
| + int i; | |
| + | |
| + if(len < 2) | |
| + return p; | |
| + | |
| + for(i = 1; i < strlen(p); i++) { | |
| + if(p[i - 1] == '.' && p[i] == '.') { | |
| + if(p[i - 2] == '/') | |
| + p[i] = '/'; | |
| + if(p[i + 1] == '/') | |
| + p[i] = '/'; | |
| + if(len == 2) | |
| + p[i] = '/'; | |
| + } | |
| + } | |
| + | |
| + return p; | |
| +} | |
| + | |
| +void | |
| +logentry(char *host, char *port, char *qry, char *status) | |
| +{ | |
| + time_t tim; | |
| + struct tm *ptr; | |
| + char timstr[128]; | |
| + | |
| + if(glfd >= 0) { | |
| + tim = time(0); | |
| + ptr = localtime(&tim); | |
| + | |
| + strftime(timstr, sizeof(timstr), "%a %b %d %H:%M:%S %Z %Y", | |
| + ptr); | |
| + | |
| + tprintf(glfd, "[%s|%s:%s] %s (%s)\n", | |
| + timstr, host, port, qry, status); | |
| + } | |
| + | |
| + return; | |
| +} | |
| + | |
| +void | |
| +handlerequest(int sock, char *base, char *ohost, char *port, char *clienth, | |
| + char *clientp) | |
| +{ | |
| + struct stat dir; | |
| + char recvc[1024], recvb[1024], path[1024], *args, *sear, *c; | |
| + int len, fd; | |
| + filetype *type; | |
| + | |
| + memset(&dir, 0, sizeof(dir)); | |
| + memset(recvb, 0, sizeof(recvb)); | |
| + memset(recvc, 0, sizeof(recvc)); | |
| + | |
| + len = recv(sock, recvb, sizeof(recvb), 0); | |
| + if(len > 1) { | |
| + if(recvb[len - 2] == '\r') | |
| + recvb[len - 2] = '\0'; | |
| + if(recvb[len - 1] == '\n') | |
| + recvb[len - 1] = '\0'; | |
| + } | |
| + strcpy(recvc, recvb); | |
| + | |
| + if(!strncmp(recvb, "URL:", 4)) { | |
| + len = snprintf(path, sizeof(path), htredir, | |
| + recvb + 4, recvb + 4, recvb + 4); | |
| + if(len > sizeof(path)) | |
| + len = sizeof(path); | |
| + send(sock, path, len, 0); | |
| + if(loglvl & HTTP) | |
| + logentry(clienth, clientp, recvc, "HTTP redirect"); | |
| + return; | |
| + } | |
| + | |
| + sear = strchr(recvb, '\t'); | |
| + if(sear != nil) | |
| + *sear++ = '\0'; | |
| + args = strchr(recvb, '?'); | |
| + if(args != nil) | |
| + *args++ = '\0'; | |
| + else | |
| + args = ohost; | |
| + | |
| + securepath(recvb, len - 2); | |
| + snprintf(path, sizeof(path), "%s%s", base, recvb); | |
| + if(stat(path, &dir) != -1 && S_ISDIR(dir.st_mode)) | |
| + strncat(path, indexf, sizeof(path) - strlen(path)); | |
| + | |
| + fd = open(path, O_RDONLY); | |
| + if(fd >= 0) { | |
| + close(fd); | |
| + if(loglvl & FILES) | |
| + logentry(clienth, clientp, recvc, "serving"); | |
| + | |
| + c = strrchr(path, '/'); | |
| + if(c == nil) | |
| + c = path; | |
| + type = gettype(c); | |
| + type->f(sock, path, port, base, args, sear); | |
| + } else { | |
| + if(S_ISDIR(dir.st_mode)) { | |
| + handledir(sock, path, port, base, args, sear); | |
| + if(loglvl & DIRS) | |
| + logentry(clienth, clientp, recvc, | |
| + "dir listing"); | |
| + return; | |
| + } | |
| + | |
| + send(sock, err, strlen(err), 0); | |
| + if(loglvl & ERRORS) | |
| + logentry(clienth, clientp, recvc, "not found"); | |
| + close(sock); | |
| + } | |
| + | |
| + return; | |
| +} | |
| + | |
| +void | |
| +hndlsigchld(int signo) | |
| +{ | |
| + int status; | |
| + | |
| + while(waitpid(-1, &status, WNOHANG) > 0); | |
| + | |
| + return; | |
| +} | |
| + | |
| +void | |
| +usage(void) | |
| +{ | |
| + | |
| + tprintf(2, "usage: %s [-d] [-l logfile] [-v loglvl] [-b base]" | |
| + " [-p port] [-o sport] [-u user] [-g group] [-h host]" | |
| + " [-i IP]\n", | |
| + argv0); | |
| + | |
| + exit(1); | |
| +} | |
| + | |
| +int | |
| +main(int argc, char *argv[]) | |
| +{ | |
| + struct addrinfo hints, *ai; | |
| + struct sockaddr_storage clt; | |
| + socklen_t cltlen; | |
| + int sock, list, opt, dofork; | |
| + char *port, *base, *logfile, clienth[NI_MAXHOST], clientp[NI_MAXSERV]; | |
| + char *user, *group, *bindip, *ohost, *sport; | |
| + struct passwd *us; | |
| + struct group *gr; | |
| + | |
| + base = stdbase; | |
| + port = stdport; | |
| + dofork = 1; | |
| + logfile = nil; | |
| + user = nil; | |
| + group = nil; | |
| + us = nil; | |
| + gr = nil; | |
| + bindip = nil; | |
| + ohost = nil; | |
| + sport = port; | |
| + | |
| + ARGBEGIN { | |
| + case 'b': | |
| + base = EARGF(usage()); | |
| + break; | |
| + case 'p': | |
| + port = EARGF(usage()); | |
| + break; | |
| + case 'l': | |
| + logfile = EARGF(usage()); | |
| + break; | |
| + case 'd': | |
| + dofork = 0; | |
| + break; | |
| + case 'v': | |
| + loglvl = atoi(EARGF(usage())); | |
| + break; | |
| + case 'u': | |
| + user = EARGF(usage()); | |
| + break; | |
| + case 'g': | |
| + group = EARGF(usage()); | |
| + break; | |
| + case 'i': | |
| + bindip = EARGF(usage()); | |
| + break; | |
| + case 'h': | |
| + ohost = EARGF(usage()); | |
| + break; | |
| + case 'o': | |
| + sport = EARGF(usage()); | |
| + break; | |
| + default: | |
| + usage(); | |
| + } ARGEND; | |
| + | |
| + if(group != nil) { | |
| + if((gr = getgrnam(group)) == nil) { | |
| + perror("no such group"); | |
| + return 1; | |
| + } | |
| + } | |
| + | |
| + if(user != nil) { | |
| + if((us = getpwnam(user)) == nil) { | |
| + perror("no such user"); | |
| + return 1; | |
| + } | |
| + } | |
| + | |
| + if(dofork && fork() != 0) | |
| + return 0; | |
| + | |
| + if(logfile != nil) { | |
| + glfd = initlogging(logfile); | |
| + if(glfd < 0) { | |
| + perror("initlogging"); | |
| + return 1; | |
| + } | |
| + } | |
| + | |
| + memset(&hints, 0, sizeof(hints)); | |
| + hints.ai_flags |= AI_PASSIVE; | |
| + hints.ai_socktype = SOCK_STREAM; | |
| + hints.ai_protocol = IPPROTO_TCP; | |
| + hints.ai_family = AF_INET; | |
| + if(getaddrinfo(bindip, port, &hints, &ai)) { | |
| + perror("getaddrinfo"); | |
| + return 1; | |
| + } | |
| + if(ai == nil) { | |
| + perror("getaddrinfo"); | |
| + return 1; | |
| + } | |
| + | |
| + list = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |
| + if(list < 0) { | |
| + perror("socket"); | |
| + return 1; | |
| + } | |
| + | |
| + opt = 1; | |
| + if(setsockopt(list, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { | |
| + perror("setsockopt"); | |
| + return 1; | |
| + } | |
| + | |
| + if(bind(list, ai->ai_addr, ai->ai_addrlen)) { | |
| + perror("bind"); | |
| + return 1; | |
| + } | |
| + | |
| + if(listen(list, 255)) { | |
| + perror("listen"); | |
| + return 1; | |
| + } | |
| + | |
| + freeaddrinfo(ai); | |
| + | |
| + if(dropprivileges(gr, us) < 0) { | |
| + perror("cannot drop privileges"); | |
| + return 1; | |
| + } | |
| + | |
| + if(dofork) { | |
| + signal(SIGINT, SIG_IGN); | |
| + signal(SIGQUIT, SIG_IGN); | |
| + } | |
| + signal(SIGCHLD, hndlsigchld); | |
| + | |
| + cltlen = sizeof(clt); | |
| + for(;;) { | |
| + sock = accept(list, (struct sockaddr *)&clt, &cltlen); | |
| + if(sock < 0) { | |
| + perror("accept"); | |
| + close(list); | |
| + return 1; | |
| + } | |
| + | |
| + getnameinfo((struct sockaddr *)&clt, cltlen, clienth, | |
| + sizeof(clienth), clientp, sizeof(clientp), | |
| + NI_NUMERICHOST); | |
| + | |
| + switch(fork()) { | |
| + case -1: | |
| + perror("fork"); | |
| + close(sock); | |
| + break; | |
| + case 0: | |
| + handlerequest(sock, base, ohost, sport, clienth, | |
| + clientp); | |
| + return 1; | |
| + default: | |
| + wait(&opt); | |
| + close(sock); | |
| + break; | |
| + } | |
| + } | |
| + | |
| + close(list); | |
| + if(logfile != nil) | |
| + stoplogging(glfd); | |
| + return 0; | |
| +} | |
| + | |
| diff --git a/rc.d/Archlinux.conf.d b/rc.d/Archlinux.conf.d | |
| @@ -0,0 +1,4 @@ | |
| +# | |
| +# Parameters to be passed to geomyidae | |
| +# | |
| +GEOMYIDAE_ARGS="-u nobody -g nobody -b /srv/gopher -o 70 -l /var/log/geomyidae… | |
| diff --git a/rc.d/Archlinux.rc.d b/rc.d/Archlinux.rc.d | |
| @@ -0,0 +1,38 @@ | |
| +#!/bin/bash | |
| + | |
| +. /etc/rc.conf | |
| +. /etc/rc.d/functions | |
| +. /etc/conf.d/geomyidae | |
| + | |
| +PID=`pidof -o %PPID /usr/bin/geomyidae` | |
| +case "$1" in | |
| + start) | |
| + stat_busy "Starting geomyidae" | |
| + [ -z "$PID" ] && /usr/bin/geomyidae $GEOMYIDAE_ARGS 2>&1 | |
| + if [ $? -gt 0 ]; then | |
| + stat_fail | |
| + else | |
| + PID=`pidof -o %PPID /usr/bin/geomyidae` | |
| + echo $PID >/var/run/geomyidae.pid | |
| + add_daemon geomyidae | |
| + stat_done | |
| + fi | |
| + ;; | |
| + stop) | |
| + stat_busy "Stopping geomyidae" | |
| + [ ! -z "$PID" ] && kill $PID &>/dev/null | |
| + if [ $? -gt 0 ]; then | |
| + stat_fail | |
| + else | |
| + rm_daemon geomyidae | |
| + stat_done | |
| + fi | |
| + ;; | |
| + restart) | |
| + $0 stop | |
| + $0 start | |
| + ;; | |
| + *) | |
| + echo "usage: $0 {start|stop|restart}" | |
| +esac | |
| +exit 0 | |
| diff --git a/rc.d/Gentoo.conf.d b/rc.d/Gentoo.conf.d | |
| @@ -0,0 +1,5 @@ | |
| +# | |
| +# Parameters to be passed to geomyidae | |
| +# | |
| +GEOMYIDAE_ARGS="-u gopherd -g gopherd -b /var/gopher -o 70 -l /var/log/geomyid… | |
| + | |
| diff --git a/rc.d/Gentoo.init.d b/rc.d/Gentoo.init.d | |
| @@ -0,0 +1,17 @@ | |
| +#!/sbin/runscript | |
| +# Copyright 1999-2010 Gentoo Foundation | |
| +# Distributed under the terms of the GNU General Public License v2 | |
| +# $Header: $ | |
| + | |
| +start(){ | |
| + ebegin "Starting geomyidae" | |
| + [ -n "$GEOMYIDAE_ARGS" ] && GEOMYIDAE_ARGS="-- $GEOMYIDAE_ARGS" | |
| + start-stop-daemon --start --pidfile /var/run/geomyidae.pid --exec /usr/sbi… | |
| + eend $? "Failed to start geomyidae" | |
| +} | |
| + | |
| +stop(){ | |
| + ebegin "Stopping geomyidae" | |
| + start-stop-daemon --stop --pidfile /var/run/geomyidae.pid | |
| + eend $? "Failed to stop geomyidae" | |
| +} | |
| diff --git a/rc.d/NetBSD.rc.d b/rc.d/NetBSD.rc.d | |
| @@ -0,0 +1,55 @@ | |
| +#!/bin/sh | |
| +# | |
| + | |
| +# REQUIRE: local | |
| +# PROVIDE: geomyidae | |
| + | |
| +$_rc_subr_loaded . /etc/rc.subr | |
| + | |
| +name="geomyidae" | |
| +rcvar=$name | |
| +command="/usr/pkg/sbin/${name}" | |
| + | |
| +##################################################### | |
| +# Geomyidae Options Section - "?" => geomyidae(8) # | |
| +# Uncomment & define options (defaults are shown) # | |
| +##################################################### | |
| +# | |
| +#LOGFILE="-l /var/log/gopherd.log" | |
| +#LOGLEVEL="-v 15" | |
| +#HTDOCS="-b /var/gopher" | |
| +#PORT="-p 70" | |
| +#SPORT="-o 70" | |
| +#USR="-u $USER" | |
| +#GRP="-g $GROUP" | |
| +#HOST="-h localhost" | |
| +#IP="-i 127.0.0.1" | |
| + | |
| +###################################################### | |
| +# Now remove any UNDEFINED options from line below: # | |
| +###################################################### | |
| +# | |
| +command_args="$LOGFILE $LOGLEVEL $HTDOCS $PORT $SPORT $USR $GRP $HOST $IP" | |
| + | |
| + | |
| +###################################################### | |
| +# Uncomment this section if a PID file is desired # | |
| +###################################################### | |
| + | |
| +#pidfile="/var/run/${name}.pid" | |
| +#start_cmd="geomyidae_start" | |
| +# | |
| +#geomyidae_start() | |
| +#{ | |
| +# echo "Starting $name" | |
| +# $command $command_args | |
| +# pgrep -x $name > $pidfile | |
| +#} | |
| + | |
| +###################################################### | |
| +# Lastly, add the following to /etc/rc.conf: # | |
| +# "geomyidae=YES" (without the quotes) # | |
| +###################################################### | |
| + | |
| +load_rc_config $name | |
| +run_rc_command "$1" | |
| diff --git a/rc.d/README b/rc.d/README | |
| @@ -0,0 +1,5 @@ | |
| +These are init scripts, used in various distribution, for Geomyidae. | |
| + | |
| +All files are examples sent in by users. Please review them before | |
| +using. | |
| + |