Copyright (C) 1999-2000 Steinar H. Gunderson
This program is is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2 if the
+ it under the terms of the GNU General Public License, version 2 of the
License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
#include <config.h>
#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
#if HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stropts.h>
#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
#if HAVE_SYS_CONF_H
#include <sys/conf.h>
#endif
#include <unistd.h>
#endif
+#if HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <sys/stat.h>
#endif
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
-#if HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif
-
-#if HAVE_NETINET_IP_H
-#include <netinet/ip.h>
-#endif
-
-#if HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-
#if HAVE_LINUX_SOCKET_H
#include <linux/socket.h>
#endif
+#if HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#endif
+
#if HAVE_MMAP
#include <sys/mman.h>
#endif
#include <ascii.h>
#endif
-/* Debug printing -- remove before 0.0.8 final */
-#if 1
-#define DPRINT(s)
-#else
-#define DPRINT(s) printf s
+#if WANT_DCACHE
+#include <dcache.h>
#endif
#ifndef MAP_FAILED
FILE *xferlog = NULL;
#endif
+#if HAVE_LINUX_SENDFILE
+int sendfile_supported = 1;
+#endif
+
/*
* This variable specifies if it's soon time to check for timed out
* clients, and timed out directory listing cache entries. It is
*/
int add_fd(const int fd, const int events)
{
- DPRINT(("add_fd(%d, %x)\n", fd, events));
#if HAVE_POLL
if (fd >= FD_MAX) {
printf("add_fd(%d, %x): failed\n", fd, events);
*/
void del_fd(const int fd)
{
- DPRINT(("del_fd(%d)\n", fd));
#if HAVE_POLL
if (fd >= FD_MAX)
return;
{
const unsigned int one = 1;
struct conn *c = (struct conn *)(malloc(sizeof(struct conn)));
- DPRINT(("alloc_new_conn(%d)\n", sock));
if (c == NULL) return c;
return f;
}
-#if WANT_DCACHE
-/*
- * alloc_new_dcache():
- * Allocates a new directory cache entry (type `struct dcache'),
- * and adds it to the linked list.
- */
-struct dcache *alloc_new_dcache()
-{
- struct dcache *d = (struct dcache *)(malloc(sizeof(struct dcache)));
-
- if (d == NULL) return d;
-
- d->use_count = 0;
- d->last_used = 0;
- strcpy(d->dir_name, "");
- d->dir_data = NULL;
-
- add_to_linked_list((struct list_element *)first_dcache,
- (struct list_element *)d);
-
- return d;
-}
-#endif
-
/*
* destroy_conn():
* Destroy a control connection, remove it from the linked
*/
void destroy_conn(struct conn * const c)
{
- DPRINT(("destroy_conn:\n"));
if (c == NULL) return;
del_fd(c->sock);
destroy_ftran(c->transfer);
remove_from_linked_list((struct list_element *)c);
-
- DPRINT(("destroy_conn done.\n"));
}
/*
remove_from_linked_list((struct list_element *)f);
}
-#if WANT_DCACHE
-/*
- * destroy_dcache():
- * Destroy a directory listing cache entry, remove it from the
- * linked list, and clean up after it.
- *
- * If you free a cache entry that is in use (use_count > 0),
- * BetaFTPD will most likely crash (later). The thing you're supposed
- * to do when you're done with a dcache entry, is to decrement
- * its use_count, and let the timeout functions do the destroying
- * when it's time to do so.
- */
-void destroy_dcache(struct dcache * const d)
-{
- if (d == NULL) return;
-
- if (d->dir_data != NULL) free(d->dir_data);
- remove_from_linked_list((struct list_element *)d);
-}
-#endif
-
/*
* process_all_clients():
- * Processes all the control connections in active_clients
+ * Processes all the _control_ connections in active_clients
* (normally returned from a select(), there are at max
* NUM_AC active connections in the set), sending them
* through to the command parser if a command has been
struct conn *c = NULL, *next = first_conn->next_conn;
int checked_through = 0;
- DPRINT(("process_all_clients: num_ac %d\n", num_ac));
-
/* run through the linked list */
while (next != NULL && checked_through < num_ac) {
int bytes_avail;
c = next;
next = c->next_conn;
#if HAVE_POLL
- if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
- destroy_conn(c);
+ if ((fds[c->sock].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) == 0) {
continue;
}
- if (!fds[c->sock].revents & POLLIN) {
- continue;
- }
- DPRINT(("process_all_clients: fd %d has POLLIN set\n", c->sock));
#else
if (!FD_ISSET(c->sock, active_clients)) {
continue;
* client has closed the socket. If we get a return value
* of -1 (error), we close the socket ourselves.
*
- * Just to be safe, we include this code for poll() as
- * well.
+ * We do the same for poll(), even though we actually have
+ * bits that tell us what is happening (in case of new
+ * input AND error/hangup at the same time, we do an
+ * explicit check at the bottom of the loop as well).
*/
destroy_conn(c);
continue;
c->buf_len += bytes_avail;
parse_command(c);
+
+ if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
+ destroy_conn(c);
+ }
}
return checked_through;
}
+/*
+ * finish_transfer():
+ * Send a message that the transfer is completed, write xferlog
+ * entry (optional), and update the last_transfer record in the
+ * file transfer object. Goes for both uploads and downloads.
+ */
+void finish_transfer(struct ftran * const f)
+{
+ numeric(f->owner, 226, "Transfer complete.");
+ time(&(f->owner->last_transfer));
+
+#if WANT_XFERLOG
+ if (!f->dir_listing) {
+ write_xferlog(f);
+ }
+#endif
+
+ destroy_ftran(f);
+#if WANT_FULLSCREEN
+ update_display(first_conn);
+#endif
+}
+
/*
* process_all_sendfiles():
* Sends data to all clients that are ready to receive it.
f = next;
next = f->next_ftran;
+#if HAVE_UPLOAD
+ if (f->upload == 1 && fds[f->sock].revents & POLLHUP) {
+ finish_transfer(f);
+ continue;
+ }
+#endif
+
#if HAVE_POLL
- if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) {
+ if (fds[f->sock].revents & (POLLERR|POLLNVAL|POLLHUP)) {
destroy_ftran(f);
continue;
}
/* state = 2: incoming PASV, state >3: send file */
#if HAVE_POLL
- if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) {
+ if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) {
#else
if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) {
#endif
if (do_download(f)) continue;
/* do_{upload,download} returned 0, the transfer is complete */
- numeric(f->owner, 226, "Transfer complete.");
- time(&(f->owner->last_transfer));
-
-#if WANT_XFERLOG
- if (!f->dir_listing) {
- write_xferlog(f);
- }
-#endif
-
- destroy_ftran(f);
+ finish_transfer(f);
#if WANT_FULLSCREEN
update_display(first_conn);
#endif
int do_upload(struct ftran *f)
{
char upload_buf[16384];
- int avail, size;
+ int size;
#if WANT_ASCII
/* keep buffer size small in ascii transfers
to prevent process stalling while filtering
* Here we use a rather simplified sending `algorithm',
* leaving most of the quirks to the system calls.
*/
- if (f->dir_listing == 0
-#if WANT_UPLOAD
- && f->upload == 0
-#endif
- ) {
+ if (sendfile_supported == 1 && f->dir_listing == 0) {
int err;
size = f->size - f->pos;
alarm(60);
signal(SIGALRM, handle_alarm);
+#if HAVE_LINUX_SENDFILE
+ /* check that sendfile() is really implemented (same check as configure does) */
+ {
+ int out_fd = 1, in_fd = 0;
+ off_t offset = 0;
+ size_t size = 1024;
+
+ errno = 0;
+ sendfile(out_fd, in_fd, &offset, size);
+ if (errno == ENOSYS) sendfile_supported = 0;
+ }
+#endif
+
for ( ;; ) {
int i;
#ifndef HAVE_POLL
#if HAVE_POLL
i = poll(fds, highest_fds + 1, 60000);
- DPRINT(("poll returns %d\n", i));
#if 0
{
int j;
}
}
-#if WANT_DCACHE
-/*
- * time_out_dcache():
- * Time out expired directory listing cache entries.
- * Uses much of the same code as time_out_sockets().
- */
-void time_out_dcache()
-{
- struct dcache *d = NULL, *next = first_dcache->next_dcache;
- time_t now = time(NULL);
-
- /* run through the linked list */
- while (next != NULL) {
- d = next;
- next = d->next_dcache;
-
- if (d->use_count == 0 && (now - d->last_used > 900)) {
- destroy_dcache(d);
- }
- }
-}
-#endif
-
/*
* remove_bytes():
* Remove some bytes from the incoming buffer. This gives
i = vsnprintf(buf, 256, fmt, args);
va_end(args);
- DPRINT((buf));
err = send(c->sock, buf, i, 0);
if (err == -1 && errno == EPIPE) {
destroy_conn(c);
#if HAVE_MMAP
if (f->dir_listing == 0) {
#if HAVE_LINUX_SENDFILE
- int do_mmap = 0;
+ int do_mmap = (sendfile_supported) ? 0 : 1;
#else
int do_mmap = 1;
#endif
* dump_file(): Dumps a file on the control connection. Used for
* welcome messages and the likes. Note that outbuf
* is so big, to prevent any crashing from users creating
- * weird .message files (like 1024 LFs)...
+ * weird .message files (like 1024 LFs)... The size of
+ * the file is limited to 1024 bytes (by truncation).
*/
void dump_file(struct conn * const c, const int num, const char * const filename)
{
- char buf[1024], outbuf[8192];
+ char buf[1024], outbuf[5121];
char *ptr = outbuf + 4;
int i, j = -1;
const time_t now = time(NULL);
int i;
- if (glob("README*", 0, NULL, &pglob) == 0) {
- for (i = 0; i < pglob.gl_pathc; i++) {
- const char * const temp = pglob.gl_pathv[i];
- struct stat buf;
- char str[2048];
+ if (glob("README*", 0, NULL, &pglob) != 0) return;
- char *tm;
+ for (i = 0; i < pglob.gl_pathc; i++) {
+ struct stat buf;
+ char str[256];
+ char *tm;
- if (stat(temp, &buf) == -1) continue;
+ if (stat(pglob.gl_pathv[i], &buf) == -1) continue;
- /* remove trailing LF */
- tm = ctime(&buf.st_mtime);
- tm[strlen(tm) - 1] = 0;
+ /* remove trailing LF */
+ tm = ctime(&buf.st_mtime);
+ tm[strlen(tm) - 1] = 0;
- sprintf(str, "250-Please read the file %s\r\n"
- "250-\tIt was last modified %s - %ld days ago\r\n",
- temp, tm,
- (now - buf.st_mtime) / 86400);
- send(c->sock, str, strlen(str), 0);
- }
- globfree(&pglob);
- }
+ snprintf(str, 256, "250-Please read the file %s\r\n"
+ "250-\tIt was last modified %s - %ld days ago\r\n",
+ pglob.gl_pathv[i], tm,
+ (now - buf.st_mtime) / 86400);
+ send(c->sock, str, strlen(str), 0);
+ }
+ globfree(&pglob);
}
#endif