/* ratio.c Basic ratio implementation for muddleftpd Copyright (C) 1999 Beau Kuiper This program 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 2, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ftpd.h" #include "reply.h" extern int doesdint; int lockarea(int fd, int pos, int len, int locktype, int do_wait) { struct flock lock; register int err; int lockt; lockt = (do_wait == TRUE ? F_SETLKW : F_SETLK); lock.l_type = locktype; lock.l_whence = SEEK_SET; lock.l_start = pos; lock.l_len = len; NOSIGNALINTR(err = fcntl(fd, lockt, &lock)); if (err == -1) { if (errno != EAGAIN) ERRORMSGFATAL(safe_snprintf("Locking failed: %s", strerror(errno))); else return(FALSE); } return(TRUE); } void lock_n_read(RATIOHANDLE *rh) { if (rh->ratiofilefd != 0) { lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); lseek(rh->ratiofilefd, rh->filepos, SEEK_SET); read(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA)); } } void write_n_unlock(RATIOHANDLE *rh) { if (rh->ratiofilefd != 0) { lseek(rh->ratiofilefd, rh->filepos, SEEK_SET); write(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA)); lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE); } } int openratiofile(char *filename) { int fd; if (filename == NULL) return(-1); fd = open(filename, O_CREAT | O_RDWR, 0600); if (fd == -1) { char *err = safe_snprintf("Error opening ratio file '%s': %s", filename, strerror(errno)); log_addentry(MYLOG_INFO, NULL, err); freewrapper(err); } return(fd); } int decoderatiostr(char *ratstr, int *pa, int *pb) { if (ratstr == NULL) return(FALSE); strtrimspace(ratstr); return(sscanf(ratstr, "%d:%d", pa, pb) == 2); } /* this function really is too complex and too long, fix later */ RATIOHANDLE *ratio_loaduser(char *username, int section) { RATIOHANDLE *newrh = mallocwrapper(sizeof(RATIOHANDLE)); RATIOFILEDATA *filedata = mallocwrapper(sizeof(RATIOFILEDATA)); char *data, *defint, *byteratio, *fileratio; int pos = 0; long long defaultdownload; int upbytes, downbytes, upfiles, downfiles, initfiles; int flags = 0; if (!doesdint) goto error; /* load the config */ loadstrfromconfig(config->configfile, section, "ratiofile", &data, NULL); loadstrfromconfig(config->configfile, section, "byteratios", &byteratio, NULL); loadstrfromconfig(config->configfile, section, "fileratios", &fileratio, NULL); if (fileratio) { if (!decoderatiostr(fileratio, &upfiles, &downfiles)) goto error; flags |= FILECREDITS; loadintfromconfig(config->configfile, section, "initalfiles", &initfiles, 0); initfiles *= downfiles; } if (byteratio) { if (!decoderatiostr(byteratio, &upbytes, &downbytes)) goto error; flags |= BYTECREDITS; loadstrfromconfig(config->configfile, section, "initalbytes", &defint, "0"); sscanf(defint, "%lld", &defaultdownload); defaultdownload *= downbytes; } newrh->rdat = filedata; newrh->ratiofilefd = 0; /* this is for non-persistant ratios */ if (!data) goto newrecord; newrh->ratiofilefd = openratiofile(data); if (newrh->ratiofilefd == -1) goto error; lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); while(read(newrh->ratiofilefd, filedata, sizeof(RATIOFILEDATA)) == sizeof(RATIOFILEDATA)) { lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE); if (strcmp(filedata->username, username) == 0) { newrh->filepos = pos; return(newrh); } pos += sizeof(RATIOFILEDATA); lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); } newrecord: /* we need to create a record, the area we want is already locked */ /* if the area gets written to disk, make sure it is clear so we don't accidently write a clear password to disk that may have been in the memory! */ memset(filedata, 0, sizeof(RATIOFILEDATA)); strncpy(filedata->username, username, MAXNAMELEN); filedata->username[MAXNAMELEN] = 0; filedata->flags = flags; if (flags & FILECREDITS) { filedata->filecredits = initfiles; filedata->fileinmult = (short int)upfiles; filedata->fileoutmult = (short int)downfiles; } if (flags & BYTECREDITS) { filedata->downloadcredits = defaultdownload; filedata->byteinmult = (short int)upbytes; filedata->byteoutmult = (short int)downbytes; } newrh->filepos = pos; write_n_unlock(newrh); return(newrh); error: freewrapper(newrh); freewrapper(filedata); return(NULL); } /* This looks expensive!, but isn't really too bad because of kernel writeback cache. */ void ratio_uploadbytes(RATIOHANDLE *rh, int bytes) { lock_n_read(rh); if (rh->rdat->flags & BYTECREDITS) rh->rdat->downloadcredits += bytes * rh->rdat->byteinmult; write_n_unlock(rh); } int ratio_downloadbytes(RATIOHANDLE *rh, int bytes) { int ret = FALSE; lock_n_read(rh); if (rh->rdat->flags & BYTECREDITS) { ret = ((rh->rdat->downloadcredits - (bytes * rh->rdat->byteoutmult)) < 0LL); if (!ret) rh->rdat->downloadcredits -= bytes * rh->rdat->byteoutmult; } write_n_unlock(rh); return(ret); } void ratio_uploadfile(RATIOHANDLE *rh) { lock_n_read(rh); if (rh->rdat->flags & FILECREDITS) rh->rdat->filecredits += rh->rdat->fileinmult; write_n_unlock(rh); } int ratio_downloadfile(RATIOHANDLE *rh) { int ret = FALSE; lock_n_read(rh); if (rh->rdat->flags & FILECREDITS) { ret = (rh->rdat->fileoutmult > rh->rdat->filecredits); if (!ret) rh->rdat->filecredits -= rh->rdat->fileoutmult; } write_n_unlock(rh); return(ret); } void ratio_reread(RATIOHANDLE *rh) { lock_n_read(rh); write_n_unlock(rh); } void ratio_stat(FTPSTATE *peer, RATIOHANDLE *rh, char *replystr) { ratio_reread(rh); if (rh->rdat->flags & BYTECREDITS) { ftp_write(peer, TRUE, 0, REPLY_RATIOBYTECREDITS(replystr, rh->rdat->downloadcredits / rh->rdat->byteoutmult)); ftp_write(peer, TRUE, 0, REPLY_RATIOBYTERATIO(replystr, rh->rdat->byteoutmult, rh->rdat->byteinmult)); } if (rh->rdat->flags & FILECREDITS) { ftp_write(peer, TRUE, 0, REPLY_RATIOFILECREDITS(replystr, rh->rdat->filecredits / rh->rdat->fileoutmult)); ftp_write(peer, TRUE, 0, REPLY_RATIOFILERATIO(replystr, rh->rdat->fileoutmult, rh->rdat->fileinmult)); } } void ratio_settokens(RATIOHANDLE *rh, TOKENSET *ts) { ratio_reread(rh); if (rh->rdat->flags & BYTECREDITS) tokenset_settoken(ts, 'a', safe_snprintf("%lld", rh->rdat->downloadcredits / rh->rdat->byteoutmult)); if (rh->rdat->flags & FILECREDITS) tokenset_settoken(ts, 'A', safe_snprintf("%d", rh->rdat->filecredits / rh->rdat->fileoutmult)); } void ratio_finish(RATIOHANDLE *rh) { freewrapper(rh->rdat); if (rh->ratiofilefd != 0) close(rh->ratiofilefd); freewrapper(rh); }