summaryrefslogtreecommitdiff
path: root/floppyd.c
diff options
context:
space:
mode:
Diffstat (limited to 'floppyd.c')
-rw-r--r--floppyd.c1297
1 files changed, 1297 insertions, 0 deletions
diff --git a/floppyd.c b/floppyd.c
new file mode 100644
index 0000000..92ca38c
--- /dev/null
+++ b/floppyd.c
@@ -0,0 +1,1297 @@
+/* Copyright 1999 Peter Schlaile.
+ * Copyright 1999-2005,2007-2009 Alain Knaff.
+ * This file is part of mtools.
+ *
+ * Mtools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mtools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * the floppyd daemon running on the local X-Server
+ *
+ * written by:
+ *
+ * Peter Schlaile
+ *
+ * udbz@rz.uni-karlsruhe.de
+ *
+ * Large parts of the network code shamelessly stolen from
+ * transproxy by John Saunders <john@nlc.net.au>
+ *
+ * Rewritten in C by Alain Knaff. Apparently C++ is still not as
+ * portable as C. */
+
+#define DEBUG 0
+
+#include "sysincludes.h"
+#include "llong.h"
+
+#ifdef USE_FLOPPYD
+
+#define USE_FLOPPYD_BUFFERED_IO 1
+#define FLOPPYD_DEFAULT_PORT 5703
+
+#include "sysincludes.h"
+#include "grp.h"
+#include <X11/Xlib.h>
+#include <X11/Xauth.h>
+
+#include "floppyd_io.h"
+
+#ifndef SIGCLD
+#define SIGCLD SIGCHLD
+#endif
+
+/* For Linux 1.2.13 */
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+/*
+ To compile:
+
+ gcc -Wall floppyd.cpp -o floppyd -lX11
+
+ floppyd
+
+ Communication to the clients works the following way:
+
+ Client sends his protocol-version. If the version between server and client
+ differ: bail out.
+
+ After that,we send our .Xauthority-file (a maximum of MAX_XAUTHORITY_LENGTH
+ Bytes long) to the server.
+
+ The server then checks, if it already has a .Xauthority file. If so
+ it is interpreted as LOCK-File for the floppy-device and the communication
+ gets terminated.
+
+ (What if we have 2 floppy devices? Well. Two floppy users with different
+ home-directories should work nicely...)
+
+ Now, the data is written to the .Xauthority file. Then we try to open
+ a connection to the local X-Server. If this fails -> bail out.
+
+ ***
+
+ The data packets are built as follows:
+
+ Base-packets: 1 Dword length, then data.
+ length is in Network-Byte order. (4 Bytes)
+
+ Commands are: 1. Packet Opcode (length 1), 1. Data packet as parameter.
+
+ Return: 1.Packet: 1. Dword: Bytes processed, 2. Dword: Error-Code
+
+ ***
+
+ TODO:
+ * Implement some IOCTL calls to format floppy disks or so...
+ * Read is somewhat dirty implemented. Tries multiple times to
+ read the expected bytes from the socket stream. Don't know
+ why this is necessary. Maybe the socket stream is nonblocking
+ or something IT SHOULD NOT BE!
+
+*/
+
+typedef unsigned char Byte;
+typedef unsigned long Dword;
+typedef mt_off_t Qword;
+
+#define MAX_XAUTHORITY_LENGTH 3000
+#define MAX_DATA_REQUEST 3000000
+#define BUFFERED_IO_SIZE 16348
+
+
+void serve_client(int sock, char **device_name, int n_dev, int close_stderr);
+
+
+#ifdef USE_FLOPPYD_BUFFERED_IO
+typedef struct io_buffer {
+ Byte out_buffer[BUFFERED_IO_SIZE];
+ Byte in_buffer[BUFFERED_IO_SIZE];
+
+ long in_valid;
+ long in_start;
+ long out_valid;
+
+ int handle;
+} *io_buffer;
+
+static io_buffer new_io_buffer (int _handle) {
+ io_buffer buffer;
+
+ buffer = New(struct io_buffer);
+
+ buffer->handle = _handle;
+ buffer->in_valid = buffer->in_start = 0;
+ buffer->out_valid = 0;
+ return buffer;
+}
+
+
+static void flush(io_buffer buffer) {
+ if (buffer->out_valid) {
+ if(write(buffer->handle, buffer->out_buffer, buffer->out_valid) < 0) {
+ perror("floppyd flush");
+ }
+ buffer->out_valid = 0;
+ }
+}
+
+static void free_io_buffer(io_buffer buffer) {
+ flush(buffer);
+ free(buffer);
+}
+
+
+static size_t buf_read (io_buffer buf, Byte* buffer, size_t nbytes) {
+ size_t rval;
+
+ if (nbytes <= buf->in_valid) {
+ memcpy(buffer, buf->in_buffer+buf->in_start, nbytes);
+ buf->in_valid -= nbytes;
+ buf->in_start += nbytes;
+ rval = nbytes;
+ } else {
+ if (buf->in_valid)
+ memcpy(buffer, buf->in_buffer+buf->in_start,
+ buf->in_valid);
+ nbytes -= buf->in_valid;
+ buffer += buf->in_valid;
+ if (nbytes > BUFFERED_IO_SIZE) {
+ rval = read(buf->handle, buffer, nbytes);
+ if (rval >= 0) {
+ rval += buf->in_valid;
+ }
+ buf->in_valid = buf->in_start = 0;
+ } else {
+ rval = read(buf->handle, buf->in_buffer,
+ BUFFERED_IO_SIZE);
+ if (rval >= 0) {
+ if (rval < nbytes) {
+ memcpy(buffer, buf->in_buffer, rval);
+ rval += buf->in_valid;
+ buf->in_valid = buf->in_start = 0;
+ } else {
+ size_t a;
+ memcpy(buffer, buf->in_buffer, nbytes);
+ buf->in_start = nbytes;
+ a = buf->in_valid;
+ buf->in_valid = rval-nbytes;
+ rval = a + nbytes;
+ }
+ }
+ }
+ }
+ return rval;
+}
+
+static size_t buf_write(io_buffer buf, void* buffer, size_t nbytes) {
+ if (buf->out_valid + nbytes > BUFFERED_IO_SIZE) {
+ flush(buf);
+ return write(buf->handle, buffer, nbytes);
+ }
+ memcpy(buf->out_buffer+buf->out_valid, buffer, nbytes);
+ buf->out_valid += nbytes;
+ return nbytes;
+}
+
+
+
+#else
+
+typedef int io_buffer;
+
+io_buffer new_io_buffer (int handle) {
+ return handle;
+}
+
+
+size_t buf_read (io_buffer handle, Byte* buffer, size_t nbytes) {
+ return (read(handle, buffer, nbytes));
+}
+
+size_t buf_write(io_buffer handle, void* buffer, size_t nbytes) {
+ return (write(handle, buffer, nbytes));
+}
+
+
+void free_io_buffer(io_buffer buffer) { }
+
+
+void flush(io_buffer buffer) { }
+
+#endif
+
+typedef struct Packet {
+ Byte* data;
+ Dword len;
+ Dword alloc_size;
+} *Packet;
+
+#include "byte_dword.h"
+
+static Dword read_dword(io_buffer fp)
+{
+ Byte val[4];
+ if (buf_read(fp, val, 4) < 4) {
+ return 0xffffffff;
+ }
+
+ return byte2dword(val);
+}
+
+static void write_dword(io_buffer fp, Dword parm)
+{
+ Byte val[4];
+
+ dword2byte(parm, val);
+
+ buf_write(fp, val,4);
+}
+
+
+static Packet newPacket(void)
+{
+ Packet packet;
+
+ packet = New(struct Packet);
+ packet->data = NULL;
+ packet->len = packet->alloc_size = 0;
+ return packet;
+}
+
+
+static void destroyPacket(Packet packet)
+{
+ if(packet->data)
+ free(packet->data);
+ free(packet);
+}
+
+static void kill_packet(Packet packet)
+{
+ if(packet->data)
+ free(packet->data);
+ packet->data = NULL;
+ packet->len = 0;
+ packet->alloc_size = 0;
+}
+
+static void make_new(Packet packet, unsigned long l)
+{
+ if (l < packet->alloc_size) {
+ packet->len = l;
+ return;
+ }
+ kill_packet(packet);
+ packet->len = packet->alloc_size = l;
+ packet->data = malloc(l);
+ memset(packet->data, 0, l);
+}
+
+static char send_packet(Packet packet, io_buffer fp)
+{
+ if (packet->data) {
+ write_dword(fp, packet->len);
+ buf_write(fp, packet->data, packet->len);
+ flush(fp);
+#if DEBUG
+ fprintf(stderr, "send_packet(): Size: %li\n", packet->len);
+#endif
+
+#if DEBUG
+ fprintf(stderr, "send_packet(): ");
+ for (int i = 0; i < packet->len; i++) {
+ fprintf(stderr, "%d ", packet->data[i]);
+ }
+ fprintf(stderr, "\n");
+#endif
+
+ }
+ return (packet->data != NULL);
+}
+
+static char recv_packet(Packet packet, io_buffer fp, Dword maxlength)
+{
+ int start;
+ int l;
+ Dword length = read_dword(fp);
+#if DEBUG
+ fprintf(stderr, "recv_packet(): Size: %li\n", length);
+#endif
+ if (length > maxlength || length == 0xffffffff ) {
+ return 0;
+ }
+ make_new(packet, length);
+ l = 0;
+ for (start = 0; start < length; start += l) {
+ l = buf_read(fp, packet->data+start, length-start);
+ if (l == 0) {
+ return 0;
+ }
+ }
+ if (packet->len == 0) {
+ return 0;
+ }
+#if DEBUG
+ fprintf(stderr, "*** read: %li\n", packet->len);
+#endif
+
+#if DEBUG
+ fprintf(stderr, "recv_packet(): ");
+ for (i = 0; i < packet->len; i++) {
+ fprintf(stderr, "%d ", packet->data[i]);
+ }
+ fprintf(stderr, "\n");
+#endif
+ return 1;
+}
+
+static void read_packet(Packet packet, int fd, int length) {
+ make_new(packet, length);
+ packet->len = read(fd, packet->data, packet->len);
+}
+
+static int write_packet(Packet packet, int fd) {
+ return (write(fd, packet->data, packet->len));
+}
+
+static void put_dword(Packet packet, int my_index, Dword val) {
+ dword2byte(val, packet->data+my_index);
+}
+
+static void put_qword(Packet packet, int my_index, Qword val) {
+ qword2byte(val, packet->data+my_index);
+}
+
+static Dword get_dword(Packet packet, int my_index) {
+ return byte2dword(packet->data+my_index);
+}
+
+static Qword get_qword(Packet packet, int my_index) {
+ return byte2qword(packet->data+my_index);
+}
+
+static Dword get_length(Packet packet) {
+ return packet->len;
+}
+
+static int eat(char **ptr, int *len, unsigned char c) {
+ /* remove length + size code + terminating 0 */
+ if (*len < c + 3)
+ return -1;
+ (*ptr) += c + 2;
+ (*len) -= c + 2;
+ return 0;
+}
+
+static const char *dispName;
+
+static char XAUTHORITY[]="XAUTHORITY";
+
+static char do_auth(io_buffer sock, int *version)
+{
+ int fd;
+ Display* displ;
+ Packet proto_version = newPacket();
+ Packet mit_cookie;
+ char *ptr;
+ int len;
+
+ char authFile[41]="/tmp/floppyd.XXXXXX";
+ char template[4096];
+
+ Packet reply = newPacket();
+
+ make_new(reply, 4);
+
+ if (!recv_packet(proto_version, sock, 4)) {
+ put_dword(reply, 0, AUTH_PACKETOVERSIZE);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ destroyPacket(proto_version);
+ return 0;
+ }
+
+ *version = get_dword(proto_version, 0);
+ if (*version > FLOPPYD_PROTOCOL_VERSION ||
+ *version < FLOPPYD_PROTOCOL_VERSION_OLD) {
+ /* fail if client requests a newer version than us */
+ put_dword(reply, 0, AUTH_WRONGVERSION);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ destroyPacket(proto_version);
+ return 0;
+ }
+
+ if(*version == FLOPPYD_PROTOCOL_VERSION_OLD) {
+ put_dword(reply, 0, AUTH_SUCCESS);
+ } else {
+ Dword cap = FLOPPYD_CAP_EXPLICIT_OPEN;
+ if(sizeof(mt_off_t) >= 8) {
+ cap |= FLOPPYD_CAP_LARGE_SEEK;
+ }
+ make_new(reply, 12);
+ put_dword(reply, 0, AUTH_SUCCESS);
+ put_dword(reply, 4, FLOPPYD_PROTOCOL_VERSION);
+ put_dword(reply, 8, cap);
+ }
+ send_packet(reply, sock);
+ destroyPacket(proto_version);
+
+ make_new(reply, 4);
+ mit_cookie = newPacket();
+ if (!recv_packet(mit_cookie, sock, MAX_XAUTHORITY_LENGTH)) {
+ put_dword(reply, 0, AUTH_PACKETOVERSIZE);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ destroyPacket(mit_cookie);
+ return 0;
+ }
+
+ umask(077);
+ fd = mkstemp(authFile);
+ if(fd == -1) {
+ /* Different error than file exists */
+ put_dword(reply, 0, AUTH_DEVLOCKED);
+ send_packet(reply, sock);
+ close(fd);
+ destroyPacket(reply);
+ destroyPacket(mit_cookie);
+ return 0;
+ }
+#ifdef HAVE_SETENV
+ setenv(XAUTHORITY, authFile, 1);
+#else
+ {
+ char *buffer=malloc(strlen(XAUTHORITY)+strlen(authFile)+2);
+ strcpy(buffer, XAUTHORITY);
+ strcat(buffer, "=");
+ strcat(buffer, authFile);
+ putenv(buffer);
+ }
+#endif
+
+ ptr = template;
+ ptr[4095] = 0;
+ *ptr++ = 1;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ gethostname(ptr+1, 4088);
+ len = strlen(ptr+1);
+ *ptr++ = len;
+ ptr += len;
+ *ptr++ = 0;
+ *ptr++ = 1;
+ *ptr++ = '0'; /* Display number */
+ *ptr++ = '\0';
+
+ if(write(fd, template, len+8) < len + 8) {
+ close(fd);
+ return 0;
+ }
+ ptr = (char *)mit_cookie->data;
+ len = mit_cookie->len;
+
+ if (eat(&ptr,&len,1) || /* the "type" */
+ eat(&ptr,&len,*ptr) || /* the hostname */
+ eat(&ptr,&len,*ptr)) { /* the display number */
+ destroyPacket(mit_cookie);
+ unlink(XauFileName());
+ put_dword(reply, 0, AUTH_BADPACKET);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ return 0;
+ }
+
+ if(write(fd, ptr, len) < len) {
+ close(fd);
+ return 0;
+ }
+ close(fd);
+
+ destroyPacket(mit_cookie);
+
+ displ = XOpenDisplay(dispName);
+ if (!displ) {
+ unlink(XauFileName());
+ put_dword(reply, 0, AUTH_AUTHFAILED);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ return 0;
+ }
+ XCloseDisplay(displ);
+
+ put_dword(reply, 0, AUTH_SUCCESS);
+ send_packet(reply, sock);
+ destroyPacket(reply);
+ unlink(XauFileName());
+ return 1;
+}
+
+/*
+ * Return the port number, in network order, of the specified service.
+ */
+static short getportnum(char *portnum)
+{
+ char *digits = portnum;
+ struct servent *serv;
+ short port;
+
+ for (port = 0; isdigit(*digits); ++digits)
+ {
+ port = (port * 10) + (*digits - '0');
+ }
+
+ if ((*digits != '\0') || (port <= 0))
+ {
+ if ((serv = getservbyname(portnum, "tcp")) != NULL)
+ {
+ port = ntohs(serv->s_port);
+ }
+ else
+ {
+ port = -1;
+ }
+ endservent();
+ }
+
+#if DEBUG
+ fprintf(stderr, "Port lookup %s -> %hd\n", portnum, port);
+#endif
+
+ return (port);
+}
+
+/*
+ * Return the IP address of the specified host.
+ */
+static IPaddr_t getipaddress(char *ipaddr)
+{
+ struct hostent *host;
+ IPaddr_t ip;
+
+ if (((ip = inet_addr(ipaddr)) == INADDR_NONE)
+ &&
+ (strcmp(ipaddr, "255.255.255.255") != 0))
+ {
+ if ((host = gethostbyname(ipaddr)) != NULL)
+ {
+ memcpy(&ip, host->h_addr, sizeof(ip));
+ }
+ endhostent();
+ }
+
+#if DEBUG
+ fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip);
+#endif
+
+ return (ip);
+}
+
+/*
+ * Find the userid of the specified user.
+ */
+static uid_t getuserid(char *user)
+{
+ struct passwd *pw;
+ uid_t uid;
+
+ if ((pw = getpwnam(user)) != NULL)
+ {
+ uid = pw->pw_uid;
+ }
+ else if (*user == '#')
+ {
+ uid = (uid_t)atoi(&user[1]);
+ }
+ else
+ {
+#ifdef HAVE_GETUSERID
+ id = getuserid("nobody");
+#else
+ uid = 65535;
+#endif
+ }
+
+#if DEBUG
+ fprintf(stderr, "User lookup %s -> %d\n", user, uid);
+#endif
+
+ endpwent();
+
+ return (uid);
+}
+
+/*
+ * Find the groupid of the specified user.
+ */
+static uid_t getgroupid(uid_t uid)
+{
+ struct passwd *pw;
+ gid_t gid;
+
+ if ((pw = getpwuid(uid)) != NULL)
+ {
+ gid = pw->pw_gid;
+ }
+ else
+ {
+#ifdef HAVE_GETGROUPID
+ id = getgroupid(uid);
+#else
+ gid = 65535;
+#endif
+ }
+
+#if DEBUG
+ fprintf(stderr, "Group lookup %d -> %d\n", uid, gid);
+#endif
+
+ endpwent();
+
+ return (gid);
+}
+
+/*
+ * Bind to the specified ip and port.
+ */
+static int bind_to_port(IPaddr_t bind_ip, short bind_port)
+{
+ struct sockaddr_in addr;
+ int sock;
+
+ /*
+ * Allocate a socket.
+ */
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ perror("socket()");
+ exit(1);
+ }
+
+ /*
+ * Set the SO_REUSEADDR option for debugging.
+ */
+ {
+ int on = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0) {
+ perror("setsockopt");
+ exit(1);
+ }
+ }
+
+ /*
+ * Set the address to listen to.
+ */
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(bind_port);
+ addr.sin_addr.s_addr = bind_ip;
+
+ /*
+ * Bind our socket to the above address.
+ */
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ perror("bind()");
+ exit(1);
+ }
+
+ /*
+ * Establish a large listen backlog.
+ */
+ if (listen(sock, SOMAXCONN) < 0)
+ {
+ perror("listen()");
+ exit(1);
+ }
+
+ return (sock);
+}
+
+static int sockethandle_now = -1;
+
+/*
+ * Catch alarm signals and exit.
+ */
+static void alarm_signal(int a)
+{
+ if (sockethandle_now != -1) {
+ close(sockethandle_now);
+ sockethandle_now = -1;
+ unlink(XauFileName());
+ }
+ exit(1);
+}
+
+
+/*
+ * This is the main loop when running as a server.
+ */
+static void server_main_loop(int sock, char **device_name, int n_dev)
+{
+ struct sockaddr_in addr;
+ unsigned int len;
+
+ /*
+ * Ignore dead servers so no zombies should be left hanging.
+ */
+ signal(SIGCLD, SIG_IGN);
+
+ for (;;) {
+ int new_sock;
+ /*
+ * Accept an incoming connection.
+ */
+ len = sizeof(addr);
+ while ((new_sock = accept(sock, (struct sockaddr *)&addr, &len)) < 0){}
+
+ /*
+ * Create a new process to handle the connection.
+ */
+#if DEBUG == 0
+ switch (fork()) {
+ case -1:
+ /*
+ * Under load conditions just ignore new connections.
+ */
+ break;
+
+ case 0:
+ /*
+ * Start the proxy work in the new socket.
+ */
+#endif
+ serve_client(new_sock,device_name, n_dev, 0);
+ exit(0);
+#if DEBUG == 0
+ }
+#endif
+ /*
+ * Close the socket as the child does the handling.
+ */
+ close(new_sock);
+ new_sock = -1;
+ }
+}
+
+/*
+ * Print some basic help information.
+ */
+static void usage(char *prog, const char *opt, int ret)
+{
+ if (opt)
+ {
+ fprintf(stderr, "%s: %s\n", prog, opt);
+ }
+ fprintf(stderr, "usage: %s [-s port [-r user] [-b ipaddr]] devicename [Names of local host]\n",
+ prog);
+ fprintf(stderr, " -d Run as a server (default port 5703 + DISPLAY)\n");
+ fprintf(stderr, " -s port Run as a server bound to the specified port.\n");
+ fprintf(stderr, " -r user Run as the specified user in server mode.\n");
+ fprintf(stderr, " -b ipaddr Bind to the specified ipaddr in server mode.\n");
+ fprintf(stderr, " -l Do not attempt to connect to localhost:0 to validate connection\n");
+ exit(ret);
+}
+
+
+static char *makeDisplayName(int dispNr)
+{
+ char result[80];
+ sprintf(result, ":%d.0", dispNr);
+ return strdup(result);
+}
+
+int main (int argc, char** argv)
+{
+ int sockfd = 0;
+ int arg;
+ int run_as_server = 0;
+ IPaddr_t bind_ip = INADDR_ANY;
+ unsigned short bind_port = 0;
+ uid_t run_uid = 65535;
+ gid_t run_gid = 65535;
+ char* username = strdup("nobody");
+ int sock;
+
+ char **device_name = NULL;
+ const char *floppy0 = "/dev/fd0";
+ int n_dev;
+
+
+ /*
+ * Parse the command line arguments.
+ */
+ if(argc > 1 && !strcmp(argv[0], "--help"))
+ usage(argv[0], NULL, 0);
+ while ((arg = getopt(argc, argv, "ds:r:b:x:h")) != EOF)
+ {
+ switch (arg)
+ {
+ case 'd':
+ run_as_server = 1;
+ break;
+ case 's':
+ run_as_server = 1;
+ bind_port = getportnum(optarg);
+ break;
+
+ case 'r':
+ free(username); username = strdup(optarg);
+ run_uid = getuserid(optarg);
+ run_gid = getgroupid(run_uid);
+ break;
+
+ case 'b':
+ run_as_server = 1;
+ bind_ip = getipaddress(optarg);
+ break;
+ case 'x':
+ dispName = strdup(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0], NULL, 0);
+ break;
+ case '?':
+ usage(argv[0], NULL, 1);
+ break;
+ }
+ }
+
+ if(optind < argc) {
+ device_name = argv + optind;
+ n_dev = argc - optind;
+ } else {
+ device_name = (char **)&floppy0;
+ n_dev = 1;
+ }
+
+ if(dispName == NULL)
+ dispName = getenv("DISPLAY");
+ if(dispName==NULL && bind_port != 0)
+ dispName=makeDisplayName((unsigned short)(bind_port - 5703));
+ if(dispName==NULL)
+ dispName=":0";
+
+ if(bind_port == 0) {
+ char *p = strchr(dispName,':');
+ bind_port = FLOPPYD_DEFAULT_PORT;
+ if(p != NULL)
+ bind_port += atoi(p+1);
+ }
+
+ if(!run_as_server) {
+ struct sockaddr_in addr;
+ unsigned int len = sizeof(addr);
+
+ /* try to find out port that we are connected to */
+ if(getsockname(0, (struct sockaddr*) &addr, &len) >= 0 &&
+ len == sizeof(addr)) {
+ bind_port = ntohs(addr.sin_port);
+ }
+ }
+
+ umask(0077);
+
+ /*
+ * Test to make sure required args were provided and are valid.
+ */
+ if (run_as_server && (bind_ip == INADDR_NONE)) {
+ usage(argv[0], "The server ipaddr is invalid.", 1);
+ }
+ if (run_as_server && (bind_port == 0)) {
+ usage(argv[0], "No server port was specified (or it was invalid).", 1);
+ }
+
+
+ /*
+ * See if we should run as a server.
+ */
+ if (run_as_server) {
+ /*
+ * Start by binding to the port, the child inherits this socket.
+ */
+ sock = bind_to_port(bind_ip, bind_port);
+
+ /*
+ * Start a server process. When DEBUG is defined, just run
+ * in the foreground.
+ */
+#if DEBUG
+ switch (0)
+#else
+ switch (fork())
+#endif
+ {
+ case -1:
+ perror("fork()");
+ exit(1);
+
+ case 0:
+ /*
+ * Ignore some signals.
+ */
+ signal(SIGHUP, SIG_IGN);
+#if DEBUG
+ signal(SIGINT, SIG_IGN);
+#endif
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGCONT, SIG_IGN);
+ signal(SIGPIPE, alarm_signal);
+ /*signal(SIGALRM, alarm_signal);*/
+
+ /*
+ * Drop back to an untrusted user.
+ */
+ setgid(run_gid);
+ initgroups(username, -1);
+ setuid(run_uid);
+
+ /*
+ * Start a new session and group.
+ */
+ setsid();
+#ifdef HAVE_SETPGRP
+#ifdef SETPGRP_VOID
+ setpgrp();
+#else
+ setpgrp(0,0);
+#endif
+#endif
+#if DEBUG
+ close(2);
+ open("/dev/null", O_WRONLY);
+#endif
+ /*
+ * Handle the server main loop.
+ */
+ server_main_loop(sock, device_name, n_dev);
+
+ /*
+ * Should never exit.
+ */
+ exit(1);
+ }
+
+ /*
+ * Parent exits at this stage.
+ */
+ exit(0);
+ }
+
+ signal(SIGHUP, alarm_signal);
+#if DEBUG == 0
+ signal(SIGINT, alarm_signal);
+#endif
+ signal(SIGQUIT, alarm_signal);
+ signal(SIGTERM, alarm_signal);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGCONT, SIG_IGN);
+ signal(SIGPIPE, alarm_signal);
+ /*signal(SIGALRM, alarm_signal);*/
+
+ /* Starting from inetd */
+
+ serve_client(sockfd, device_name, n_dev, 1);
+ return 0;
+}
+
+static void send_reply(int rval, io_buffer sock, int len) {
+ Packet reply = newPacket();
+
+ make_new(reply, 8);
+ put_dword(reply, 0, len);
+ if (rval == -1) {
+ put_dword(reply, 4, 0);
+ } else {
+ put_dword(reply, 4, errno);
+ }
+ send_packet(reply, sock);
+ destroyPacket(reply);
+}
+
+static void send_reply64(int rval, io_buffer sock, mt_off_t len) {
+ Packet reply = newPacket();
+
+ make_new(reply, 12);
+ put_qword(reply, 0, len);
+ if (rval == -1) {
+ put_dword(reply, 8, 0);
+ } else {
+ put_dword(reply, 8, errno);
+ }
+ send_packet(reply, sock);
+ destroyPacket(reply);
+}
+
+static void cleanup(int x) {
+ unlink(XauFileName());
+ exit(-1);
+}
+
+#include "lockdev.h"
+
+void serve_client(int sockhandle, char **device_name, int n_dev,
+ int close_stderr) {
+ Packet opcode;
+ Packet parm;
+
+ int readOnly;
+ int devFd;
+ io_buffer sock;
+ int stopLoop;
+ int version;
+ int needSendReply=0;
+ int rval=0;
+
+ /*
+ * Set the keepalive socket option to on.
+ */
+ {
+ int on = 1;
+ if(setsockopt(sockhandle, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) {
+ perror("setsockopt");
+ exit(1);
+ }
+
+ }
+
+
+#if DEBUG == 0
+ if(close_stderr) {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+#endif
+
+ sock = new_io_buffer(sockhandle);
+
+ /*
+ * Allow 60 seconds for any activity.
+ */
+ alarm(60);
+
+ version = 0;
+ if (!do_auth(sock, &version)) {
+ free_io_buffer(sock);
+ return;
+ }
+ alarm(0);
+
+
+ signal(SIGTERM, cleanup);
+ signal(SIGALRM, cleanup);
+
+
+
+ sockethandle_now = sockhandle;
+
+
+ opcode = newPacket();
+ parm = newPacket();
+
+ devFd = -1;
+ readOnly = 1;
+
+ stopLoop = 0;
+ if(version == FLOPPYD_PROTOCOL_VERSION_OLD) {
+ /* old protocol */
+ readOnly = 0;
+ devFd = open(device_name[0], O_RDWR|O_LARGEFILE);
+
+ if (devFd < 0) {
+ readOnly = 1;
+ devFd = open(device_name[0],
+ O_RDONLY|O_LARGEFILE);
+ }
+ if(devFd < 0) {
+ send_reply(0, sock, devFd);
+ stopLoop = 1;
+ }
+ lock_dev(devFd, !readOnly, NULL);
+ }
+
+
+ while(!stopLoop) {
+ int dev_nr = 0;
+ /*
+ * Allow 60 seconds for any activity.
+ */
+ /*alarm(60);*/
+
+ if (!recv_packet(opcode, sock, 1)) {
+ break;
+ }
+/* if(opcode->data[0] != OP_CLOSE)*/
+ recv_packet(parm, sock, MAX_DATA_REQUEST);
+
+
+ switch(opcode->data[0]) {
+ case OP_OPRO:
+ if(get_length(parm) >= 4)
+ dev_nr = get_dword(parm,0);
+ else
+ dev_nr = 0;
+ if(dev_nr >= n_dev) {
+ send_reply(0, sock, -1);
+ break;
+ }
+
+ devFd = open(device_name[dev_nr],
+ O_RDONLY | O_LARGEFILE);
+#if DEBUG
+ fprintf(stderr, "Device opened\n");
+#endif
+ if(devFd >= 0 && lock_dev(devFd, 0, NULL)) {
+ send_reply(0, sock, -1);
+ break;
+ }
+ send_reply(0, sock, devFd);
+ readOnly = 1;
+ break;
+ case OP_OPRW:
+ if(get_length(parm) >= 4)
+ dev_nr = get_dword(parm,0);
+ else
+ dev_nr = 0;
+ if(dev_nr >= n_dev) {
+ send_reply(0, sock, -1);
+ break;
+ }
+ devFd = open(device_name[dev_nr], O_RDWR);
+ if(devFd >= 0 && lock_dev(devFd, 1, NULL)) {
+ send_reply(0, sock, -1);
+ break;
+ }
+ send_reply(0, sock, devFd);
+ readOnly = 0;
+ break;
+ case OP_READ:
+#if DEBUG
+ fprintf(stderr, "READ:\n");
+#endif
+ read_packet(parm, devFd, get_dword(parm, 0));
+ send_reply(devFd, sock, get_length(parm));
+ if(get_length(parm) >= 0)
+ send_packet(parm, sock);
+ break;
+ case OP_WRITE:
+#if DEBUG
+ fprintf(stderr, "WRITE:\n");
+#endif
+ if(readOnly) {
+ errno = -EROFS;
+ rval = -1;
+ } else {
+ rval = write_packet(parm, devFd);
+ }
+ send_reply(devFd, sock, rval);
+ break;
+ case OP_SEEK:
+#if DEBUG
+ fprintf(stderr, "SEEK:\n");
+#endif
+
+ lseek(devFd,
+ get_dword(parm, 0), get_dword(parm, 4));
+ send_reply(devFd,
+ sock,
+ lseek(devFd, 0, SEEK_CUR));
+ break;
+ case OP_SEEK64:
+ if(sizeof(mt_off_t) < 8) {
+#if DEBUG
+ fprintf(stderr, "64 bit requested where not available!\n");
+#endif
+ errno = EINVAL;
+ send_reply(devFd, sock, -1);
+ break;
+ }
+#if DEBUG
+ fprintf(stderr, "SEEK64:\n");
+#endif
+ mt_lseek(devFd,
+ get_qword(parm,0), get_dword(parm,8));
+ send_reply64(devFd,
+ sock,
+ mt_lseek(devFd, 0, SEEK_CUR));
+ break;
+ case OP_FLUSH:
+#if DEBUG
+ fprintf(stderr, "FLUSH:\n");
+#endif
+ fsync(devFd);
+ send_reply(devFd, sock, 0);
+ break;
+ case OP_CLOSE:
+#if DEBUG
+ fprintf(stderr, "CLOSE:\n");
+#endif
+
+ close(devFd);
+ needSendReply = 1;
+ rval = devFd;
+ devFd = -1;
+ stopLoop = 1;
+ break;
+ case OP_IOCTL:
+ /* Unimplemented for now... */
+ break;
+ default:
+#if DEBUG
+ fprintf(stderr, "Invalid Opcode!\n");
+#endif
+ errno = EINVAL;
+ send_reply(devFd, sock, -1);
+ break;
+ }
+ kill_packet(parm);
+ alarm(0);
+ }
+
+
+
+#if DEBUG
+ fprintf(stderr, "Closing down...\n");
+#endif
+
+ if (devFd >= 0) {
+ close(devFd);
+ devFd = -1;
+ }
+
+ free_io_buffer(sock);
+
+ /* remove "Lock"-File */
+ unlink(XauFileName());
+
+ if(needSendReply)
+ send_reply(rval, sock, 0);
+ destroyPacket(opcode);
+ destroyPacket(parm);
+}
+
+#else
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ puts("Floppyd support not included!");
+ return -1;
+}
+
+#endif