Shared connection state ----------------------- Richard Jones, 27th December 2001. 1. Problem ---------- Up until now (version 1.103) the architecture of the FTP server has been very simple: there is a parent process listening for connections. When a connection comes in, a process is forked off to handle the connection. Between connections there is no shared state: each connection knows nothing of any other connections in progress. Advanced features in the FTP server require some state to be shared between connections. Examples are: 1.1 The proposed SITE WHO command --------------------------------- This proposed command would allow an administrator to list out all users currently using the FTP server. Clearly the child process handling the administrator's connection must therefore know something about all of the other child processes. 1.2 The proposed `max clients' directive ---------------------------------------- To specify the maximum number of clients in a class or for a particular user, it is suggested that we extend `max clients' like so: max clients: 10 class anonymous # only 10 anonymous clients allowed max clients: 1 each class users # each person in "users" can log in # only once (no limit on max users, however) max clients: 2 user pete,rich # pete and rich can only log in twice Notice that you don't know what class a user is (or what their username is) until well into the connection. Thus to implement this, each child process must again know a lot of information about the state of each other child process. This again implies shared state. 1.3 Upload and download quotas and credits ------------------------------------------ At this point in time, it is not clear exactly what the best way to implement quotas and credits will be. However, it is clear that a user must not be able to bypass a quota by opening more than one connection to the server at the same time. In order to achieve this, each child process must know what each other child process is doing. 2. Solution ----------- There are several possible ways to share information between the child processes. They include: . shared file(s) . shared memory (SysV SHM) . shared anonymous memory-mapped files (mmap) Of these, the most robust method and the one which is available on all platforms, seems to be shared files using POSIX fcntl-style locking. For the different types of shared state, different approaches are suitable. All will use shared file(s) of some sort, but not all will require explicit locking. All shared files should be stored on local disk to avoid problems with consistency, speed and locking over NFS. A suitable place to store files would be in /var/lock/ftpd/ 2.1 Connection state -------------------- Each child process should create a file called /var/lock/ftpd/conns/$$ (where $$ is the process ID). This text file will contain information about the connection (and may be rewritten several times over the course of a single connection). When the connection is dropped, this file *must* be deleted. Other child processes may immediately find out the global connection state by opening and examining each file in the /var/lock/ftpd/conns/ directory. To ensure (a) that files cannot be read partially by other children and (b) that files may still be written to even after a child process has dropped privileges and chrooted, each child will keep a file descriptor open on the file and only append to it using atomic write(2). The contents of this file is a list of zero or more of the following `key: value's: peeraddrstring: 1.2.3.4 # Client's IP address. peerhostname: foo.bar.example.com # Client's hostname, if resolved. user: rich # Username, if authenticated. class: users # User's class. uploaded: 50000 # Number of bytes (???) uploaded. downloaded: 2000000 # Number of bytes (???) downloaded. If the same key occurs twice, then the latter key is taken (allowing the append-only semantics to work). Other keys will be added here as they are required. 2.2 Upload and download credits and quotas ------------------------------------------