#include "server.hpp" #define MAX_CONCURRENCY_LIMIT 40000 // overkill #define POLL_TIMEOUT 1000 // Milliseconds #define S2C_SERVICE_FREQ 1.0 // Seconds #define USER_DATABASE "user_database.txt" #define FILE_DATABASE "file_database.txt" #define FILES_DIR "files" int nConns; // total # of data sockets struct pollfd peers[MAX_CONCURRENCY_LIMIT + 1]; // sockets to be monitored by poll() struct CONN_STAT connStat[MAX_CONCURRENCY_LIMIT + 1]; // app-layer stats of the sockets BYTE *buf[MAX_CONCURRENCY_LIMIT + 1]; // Duplicate exists in server and client void RemoveConnection(int i) { close(peers[i].fd); if (i < nConns) { memmove(peers + i, peers + i + 1, (nConns - i) * sizeof(struct pollfd)); memmove(connStat + i, connStat + i + 1, (nConns - i) * sizeof(struct CONN_STAT)); free(buf[i]); memmove(buf + i, buf + i + 1, (nConns - i) * sizeof(BYTE *)); } nConns--; Log("[%s] %d active connections after removal", NAME, nConns); } void DoServer(int svrPort) { int maxConcurrency = 20; // TODO: Change this to be a #define int listenFD = socket(AF_INET, SOCK_STREAM, 0); if (listenFD < 0) { Error("[%s] Cannot create listening socket.", NAME); } SetNonBlockIO(listenFD); struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons((unsigned short)svrPort); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); int optval = 1; int r = setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (r != 0) { Error("[%s] Cannot enable SO_REUSEADDR option.", NAME); } signal(SIGPIPE, SIG_IGN); if (bind(listenFD, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) != 0) { Error("[%s] Cannot bind to port %d.", NAME, svrPort); } if (listen(listenFD, 16) != 0) { Error("[%s] Cannot listen to port %d.", NAME, svrPort); } nConns = 0; memset(peers, 0, sizeof(peers)); peers[0].fd = listenFD; peers[0].events = POLLRDNORM; memset(connStat, 0, sizeof(connStat)); int connID = 0; while (1) { // monitor the listening sock and data socks, nConn+1 in total int nReady = poll(peers, nConns + 1, POLL_TIMEOUT); if (nReady < 0) { Error("[%s] Invalid poll() return value.", NAME); } struct sockaddr_in clientAddr; socklen_t clientAddrLen = sizeof(clientAddr); struct timeb currentTime; ftime(¤tTime); // new incoming connection if ((peers[0].revents & POLLRDNORM) && (nConns < maxConcurrency)) { int fd = accept(listenFD, (struct sockaddr *)&clientAddr, &clientAddrLen); if (fd != -1) { SetNonBlockIO(fd); nConns++; Log("[%s] Accepted connection %d", NAME, nConns); peers[nConns].fd = fd; peers[nConns].events = POLLRDNORM; peers[nConns].revents = 0; memset(&connStat[nConns], 0, sizeof(struct CONN_STAT)); ftime(&connStat[nConns].lastTime); connStat[nConns].recInitHeader = false; // waiting to recieve initial header connStat[nConns].expectingHeader = true; // message to be read is a header connStat[nConns].nToDo = HEADER_LEN; buf[nConns] = (BYTE *)malloc(BUF_LEN); memset(buf[nConns], 0, BUF_LEN); } if (--nReady <= 0) continue; } for (int i = 1; i <= nConns; i++) { // Log("nConns: %d peers[%d].revents: %02X (POLLRDNORM %02X, POLLWRNORM // %02X, POLLERR %02X, POLLHUP %02X)", nConns, i, peers[i].revents, // POLLRDNORM, POLLWRNORM, POLLERR, POLLHUP); double timeDiff = (currentTime.time + currentTime.millitm / (double)1000.0f) - (connStat[i].lastTime.time + connStat[i].lastTime.millitm / (double)1000.0f); if (peers[i].revents & (POLLRDNORM | POLLERR | POLLHUP)) { // TODO: break out POLLERR and POLLHUP? // Log("Servicing connection %d, POLLHUP: %d", i, peers[i].revents & // POLLHUP); // Process initial communication if (connStat[i].recInitHeader == false) { if (Recv_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < 0) { Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); RemoveConnection(i); goto NEXT_CONNECTION; } if (connStat[i].nToDo == 0) { Header header; header.decode(buf[i]); // Log("Initial communication for connection %d:", i); if (!((header.m_command == CONNECT) | (header.m_command == SENDF) | (header.m_command == SENDF2) | (header.m_command == GETF))) { Log("[%s] WARNING: Unexpected initial connection command %s", NAME, com2str(header.m_command)); } connStat[i].recInitHeader = true; connStat[i].expectingHeader = true; // flag needed for processing below if (processReception(i) != 0) { Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); RemoveConnection(i); goto NEXT_CONNECTION; } } } // End initial communication else { // Standard polling recieve // Log("Standard polling recieve/send: connStat[i].direction: %02X", // connStat[i].direction); if (connStat[i].direction == C2S) { if (Recv_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < 0) { Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); RemoveConnection(i); goto NEXT_CONNECTION; } if (connStat[i].nToDo == 0) { if (processReception(i) != 0) { Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); RemoveConnection(i); goto NEXT_CONNECTION; } } } // end C2S } // End standard recieve } // if POLLRDNORM // a data socket is writable if (peers[i].revents & POLLWRNORM) { // Log("Sending nonblocking!"); if (Send_NonBlocking(peers[i].fd, buf[i], &connStat[i], &peers[i]) < 0) { Log("[%s] Line %d: Removing connection %d", NAME, __LINE__, i); RemoveConnection(i); goto NEXT_CONNECTION; } } // a s2c connection hasn't been used in a while? Check that the client is // still there with a ping if ((connStat[i].direction == S2C) && (timeDiff > S2C_SERVICE_FREQ) && (connStat[i].nToDo == 0) && (connStat[i].messageLen == 0) && (connStat[i].nBuffered == 0)) { // Log("[%s] Pinging [%s] on connection %d due to timeout", SERVER_NAME, // connStat[i].name, i); Header header; header.setFlags(S2C, SUCCESS, PRIV, SIGN); memcpy(header.m_name, NAME, MAX_USERNAME_LEN); header.m_command = PING; header.m_size = 0; header.encode(&buf[i][connStat[i].messageLen]); connStat[i].nBuffered = HEADER_LEN + header.m_size; connStat[i].messageLen = HEADER_LEN + header.m_size; connStat[i].nToDo = HEADER_LEN + header.m_size; peers[i].events |= POLLWRNORM; } NEXT_CONNECTION: asm("nop"); // if (--nReady <= 0) break; } // for } // while } // do_server // buf has all buffers // Stat has all stat structs // i is the index of the link being processed int processReception(int i) { Header header; header.decode(buf[i]); // header.displayContents(true); if (connStat[i] .expectingHeader) { // Expecting a header read sequence was completed // Log("\nProcessing header reception. header.m_size: %d", header.m_size); if (header.m_size > 0) { connStat[i].expectingHeader = false; connStat[i].nToDo = header.m_size; } else if (header.m_size == 0) { // printServerCommand(i); doServerCommand(i); connStat[i].expectingHeader = true; connStat[i].nBuffered = 0; connStat[i].nToDo = HEADER_LEN; memset(buf[i], 0, BUF_LEN); } else { return -1; // Error, signal to caller to end connection } } else { // expecting a data read sequence was completed // Log("\nProcessing data reception."); // printServerCommand(i); doServerCommand(i); if (connStat[i].direction == C2S) { // CONNECT events for S2C can get here connStat[i].expectingHeader = true; connStat[i].nBuffered = 0; connStat[i].nToDo = HEADER_LEN; memset(buf[i], 0, BUF_LEN); } } if (connStat[i].shouldClose) { Log("[%s] Line %d: Removing connection %d due to shouldClose", NAME, __LINE__, i); RemoveConnection(i); } return 0; } void doServerCommand(int i) { // Assume the buf[i] has a message at the beginning of the buffers Header header; header.decode(buf[i]); struct timeb currentTime; ftime(¤tTime); char message[5000] = {0}; char username[20] = {0}; char password[20] = {0}; Header outHeader; int numLoggedIn = 0; bool foundActiveUser = false; int s2cConnNumb = -1; char server_filename[5000] = {0}; char filename[5000] = {0}; int filenameLen, fileLen, offset, nextFileId, fileId; BYTE intbuf[5] = {0}; FILE *fp; switch (header.m_command) { case CONNECT: buf2i(&buf[i][HEADER_LEN], connStat[i].id); connStat[i].direction = header.m_direction; connStat[i].linkType = MESSAGE_LINK; connStat[i].lastTime = currentTime; if (header.m_direction == C2S) { Log("[---- : %s] id: %d, direction: C2S, connection number: %d.", com2str(header.m_command), connStat[i].id, i); connStat[i].expectingHeader = true; connStat[i].nBuffered = 0; connStat[i].nToDo = HEADER_LEN; } else { Log("[---- : %s] id: %d, direction: S2C, connection number: %d.", com2str(header.m_command), connStat[i].id, i); connStat[i].nBuffered = 0; connStat[i].messageLen = 0; connStat[i].nToDo = 0; peers[i].events &= ~POLLRDNORM; peers[i].events &= ~POLLWRNORM; } memset(buf[i], 0, BUF_LEN); break; case REGISTER: memcpy(username, header.m_name, MAX_USERNAME_LEN); memcpy(password, &buf[i][HEADER_LEN], header.m_size); if (usernameQuery(USER_DATABASE, username) == true) { Log("[---- : %s] Registration failed. Username [%s] is already " "registered.", com2str(header.m_command), username); outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = REGISTER; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Cannot register username ["); strcat(message, username); strcat(message, "]. Username already registered.\n"); strcat(message, "\tUsernames may only contain characters a-z, A-Z, or 0-9.\n"); strcat(message, "\tUsernames may be 4 to 8 characters long."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } else { Log("[---- : %s] Successful registration (usr, pswd): %s %s", com2str(header.m_command), username, password); recordEntry(USER_DATABASE, username, password); outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); outHeader.m_command = REGISTER; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Successfully registered "); strcat(message, username); strcat(message, "."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } break; case LOGIN: memcpy(username, header.m_name, MAX_USERNAME_LEN); memcpy(password, &buf[i][HEADER_LEN], header.m_size); if (loginQuery(USER_DATABASE, username, password) == true) { // Check that user is not currently logged in for (int x = 1; x <= nConns; x++) { if ((connStat[x].isLoggedIn == true) && (strcmp(username, connStat[x].name) == 0)) { Log("[%s : %s] Login failed. User is already logged in.", header.m_name, com2str(header.m_command)); foundActiveUser = true; outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = LOGIN; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Login failed. "); strcat(message, username); strcat(message, " is logged in on another device."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); break; } } if (!foundActiveUser) { for (int x = 1; x <= nConns; x++) { if (connStat[i].id == connStat[x].id) { memcpy(connStat[x].name, username, MAX_USERNAME_LEN); // Log("Connection %d (%s) is loggin in.", x, connStat[x].name); connStat[x].isLoggedIn = true; } } Log("[%s : %s] Successful login (usr, pswd): %s %s", connStat[i].name, com2str(header.m_command), username, password); outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); outHeader.m_command = LOGIN; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Welcome "); strcat(message, username); strcat(message, "."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, true, outHeader, message); } } else { Log("[---- : %s] Login failed (usr, pswd): %s %s", com2str(header.m_command), username, password); outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = LOGIN; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Login failed: Invalid username or password."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } break; case LOGOUT: if (connStat[i].isLoggedIn) { for (int x = 1; x <= nConns; x++) { if (connStat[i].id == connStat[x].id) { // Log("Connection %d (user %s) is logging off.", x, // connStat[x].name); connStat[x].isLoggedIn = false; foundActiveUser = true; } } Log("[%s : %s] Successful logout.", connStat[i].name, com2str(header.m_command)); outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); outHeader.m_command = LOGOUT; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Goodbye "); strcat(message, connStat[i].name); strcat(message, ". Come back soon."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } else { Log("[---- : %s] Failed: Connection %d is not logged in.", com2str(header.m_command), i); outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = LOGOUT; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Logout failed. You are not logged in."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } break; case SEND: case SENDA: case SEND2: case SENDA2: if (connStat[i].isLoggedIn) { outHeader.setFlags(S2C, SUCCESS, PUB, header.m_trace); outHeader.m_command = header.m_command; if (header.m_trace == SIGN) memcpy(outHeader.m_name, connStat[i].name, strlen(connStat[i].name)); else memcpy(outHeader.m_name, ANON, strlen(ANON)); memcpy(message, &buf[i][HEADER_LEN], header.m_size); outHeader.m_size = strlen(message); if ((header.m_command == SEND) || (header.m_command == SENDA)) { Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), message); sendMessageToAllLoggedIn(outHeader, message); } else { memcpy(username, header.m_name, MAX_USERNAME_LEN); Log("[%s : %s %s] %s", connStat[i].name, com2str(header.m_command), username, message); sendMessageToName(username, outHeader, message); } } else { Log("[%s : %s] Failed: Connection %d is not logged in.", connStat[i].name, com2str(header.m_command), i); outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = header.m_command; memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Users must be logged in to '"); strcat(message, com2str(header.m_command)); strcat(message, "'. Login with '"); strcat(message, com2str(LOGIN)); strcat(message, " [username] [password]'."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } break; case SENDF: // Initial connection case SENDF2: // Initial connection buf2i(&buf[i][HEADER_LEN], connStat[i].id); // Log("This connection's id: %d", connStat[i].id); connStat[i].direction = header.m_direction; connStat[i].linkType = FILE_LINK; connStat[i].lastTime = currentTime; connStat[i].expectingHeader = true; connStat[i].nBuffered = 0; connStat[i].nToDo = HEADER_LEN; connStat[i].shouldClose = true; // flag to close this connection once it's processed for (int x = 1; x <= nConns; x++) { if ((connStat[x].direction == C2S) && (connStat[x].linkType == MESSAGE_LINK) && (connStat[x].id == connStat[i].id)) { strcat(connStat[i].name, connStat[x].name); connStat[i].isLoggedIn = connStat[x].isLoggedIn; foundActiveUser = true; s2cConnNumb = x; break; } } if (s2cConnNumb == -1) { Log("Enexpected state. Recieved %s on new connection without S2C " "connection for messages.", com2str(header.m_command)); } else if (!connStat[i].isLoggedIn) { Log("[%s : %s] Failed: Connection %d is not logged in.", connStat[i].name, com2str(header.m_command), i); outHeader.setFlags(S2C, FAIL, PUB, SIGN); outHeader.m_command = SEND; // avoid file transfer logic on client side memcpy(outHeader.m_name, NAME, strlen(NAME)); strcat(message, "Users must be logged in to '"); strcat(message, com2str(header.m_command)); strcat(message, "'. Login with '"); strcat(message, com2str(LOGIN)); strcat(message, " [username] [password]'."); outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); } else { // Save the file nextFileId = getNumEntries(FILE_DATABASE); if (nextFileId == -1) { Log("Enexpected state. %s next File Id of -1.", FILE_DATABASE); } else { snprintf(server_filename, sizeof(server_filename), "%s/%d_", FILES_DIR, nextFileId); offset = HEADER_LEN + 4; buf2i(&buf[i][offset], filenameLen); // first 4 bytes is client id offset += 4; memcpy(&server_filename[strlen(server_filename)], &buf[i][offset], filenameLen); memcpy(filename, &buf[i][offset], filenameLen); offset += filenameLen; buf2i(&buf[i][offset], fileLen); offset += 4; // Log("[%s : %s] Saving file: %s", server_filename); fp = fopen(FILE_DATABASE, "a"); fputs(server_filename, fp); fputs("\n", fp); fclose(fp); buf2file(&buf[i][offset], fileLen, server_filename); // Send SENDF/SENDF2 message to interested parties outHeader.setFlags(S2C, SUCCESS, header.m_recipient, SIGN); // will update recipient as necessary outHeader.m_command = header.m_command; memcpy(outHeader.m_name, connStat[i].name, strlen(connStat[i].name)); i2buf(nextFileId, intbuf); memcpy(message, intbuf, 4); offset = 4; memcpy(&message[offset], &buf[i][HEADER_LEN + 8], filenameLen); offset += filenameLen; outHeader.m_size = offset; if (header.m_command == SENDF) { Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), filename); sendMessageToAllLoggedInExceptSender(connStat[i].id, outHeader, message); } else { memcpy(username, header.m_name, MAX_USERNAME_LEN); Log("[%s : %s %s] %s", connStat[i].name, com2str(header.m_command), username, filename); sendMessageToName(username, outHeader, message); } } } break; case GETF: buf2i(&buf[i][HEADER_LEN], connStat[i].id); // header name is origional sender. Find client's name with client id for (int x = 1; x <= nConns; x++) { if ((connStat[i].id == connStat[x].id) && (connStat[x].direction == C2S) && (connStat[x].linkType == MESSAGE_LINK)) { memcpy(connStat[i].name, connStat[x].name, MAX_USERNAME_LEN); break; } } buf2i(&buf[i][HEADER_LEN + 4], fileId); connStat[i].direction = S2C; connStat[i].linkType = FILE_LINK; connStat[i].lastTime = currentTime; // no need for shouldClose. Client will close connection and periodic ping will // detect closure snprintf(server_filename, sizeof(server_filename), "%s/%d_", FILES_DIR, fileId); memcpy(&server_filename[strlen(server_filename)], &buf[i][HEADER_LEN + 8], header.m_size - 8); memcpy(filename, &buf[i][HEADER_LEN + 8], header.m_size - 8); filenameLen = strlen(filename); // Log("Getting file %s", server_filename); memset(buf[i], 0, BUF_LEN); // Check that the file exists fp = fopen(server_filename, "r"); if (fp) { fclose(fp); outHeader.setFlags(S2C, SUCCESS, header.m_recipient, SIGN); // will update recipient as necessary if (header.m_command == SENDF2) outHeader.m_recipient = PRIV; outHeader.m_command = header.m_command; memcpy(outHeader.m_name, header.m_name, MAX_USERNAME_LEN); // pass along origional sender offset = HEADER_LEN; i2buf(filenameLen, &buf[i][offset]); // encode filename length offset += 4; memcpy(&buf[i][offset], filename, strlen(filename)); // encode the filename offset += strlen(filename); fileLen = file2buf(server_filename, &buf[i][offset + 4]); i2buf(fileLen, &buf[i][offset]); offset += 4 + fileLen; outHeader.m_size = offset - HEADER_LEN; outHeader.encode(buf[i]); connStat[i].nBuffered = offset; connStat[i].messageLen = offset; connStat[i].nToDo = offset; peers[i].events &= ~POLLRDNORM; peers[i].events |= POLLWRNORM; Log("[%s : %s] %s", connStat[i].name, com2str(header.m_command), filename); } else { Log("File not found: %s", filename); // TODO: Send message if file is not found } break; case LIST: Log("[%s : %s]", connStat[i].name, com2str(header.m_command)); outHeader.setFlags(S2C, SUCCESS, PUB, SIGN); outHeader.m_command = LIST; memcpy(outHeader.m_name, NAME, strlen(NAME)); for (int x = 1; x <= nConns; x++) { if ((connStat[x].direction == C2S) && (connStat[x].linkType == MESSAGE_LINK) && (connStat[x].isLoggedIn)) { numLoggedIn++; } } snprintf(message, sizeof(message), "%d", numLoggedIn); strcat(message, " user(s) currently logged in:"); for (int x = 1; x <= nConns; x++) { if ((connStat[x].direction == C2S) && (connStat[x].linkType == MESSAGE_LINK) && (connStat[x].isLoggedIn)) { strcat(message, "\n\t"); strcat(message, connStat[x].name); } } outHeader.m_size = strlen(message); sendMessageToId(connStat[i].id, false, outHeader, message); break; case PING: // Log("Ping from %s", header.m_name); asm("nop"); // do nothing break; default: Log("No doServerCommand() for command %s", com2str(header.m_command)); } } // Send the message to every S2C with the specified id. If the reqLoggedIn // flag is set, require that the connection is logged in. void sendMessageToId(int id, bool reqLoggedIn, Header header, char *message) { for (int i = 1; i <= nConns; i++) { if ((connStat[i].direction == S2C) && (id == connStat[i].id)) { if (reqLoggedIn) { if (connStat[i].isLoggedIn) { prepareMessage(i, header, message); } } else { prepareMessage(i, header, message); } } } } // Send the message to every S2C which is logged in. void sendMessageToAllLoggedIn(Header header, char *message) { for (int i = 1; i <= nConns; i++) { if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn) { prepareMessage(i, header, message); } } } // Send the message to every s2cConnNumb void sendMessageToAllLoggedInExceptSender(int senderID, Header header, char *message) { for (int i = 1; i <= nConns; i++) { if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn && (connStat[i].id != senderID)) { prepareMessage(i, header, message); } } } // Send the message to all loged in users with the specified name. void sendMessageToName(char *name, Header header, char *message) { for (int i = 1; i <= nConns; i++) { if ((connStat[i].direction == S2C) && connStat[i].isLoggedIn && (strcmp(name, connStat[i].name) == 0)) { prepareMessage(i, header, message); } } } // Load a message into a connection void prepareMessage(int i, Header header, char *message) { if (connStat[i].direction == S2C) { int offset = connStat[i].nBuffered; // Log("Preparing message for connection %d", i); // header.displayContents(true); // Log("Message: |%s|", message); // Log("offset: %d, nBuffered: %d, nToDo: %d: messageLen: %d", offset, // connStat[i].nBuffered, connStat[i].nToDo, connStat[i].messageLen); header.encode(&buf[i][offset]); memcpy(&buf[i][offset + HEADER_LEN], message, header.m_size); connStat[i].nBuffered += HEADER_LEN + header.m_size; if (connStat[i].nToDo == 0) { connStat[i].messageLen = HEADER_LEN + header.m_size; connStat[i].nToDo = HEADER_LEN + header.m_size; } peers[i].events |= POLLWRNORM; } else { Log("[%s] WARNING: attempted to write to C2S connection %d", NAME, i); } } void printServerCommand(int i) { Header h; h.decode(buf[i]); Log("[%s]: [%s, %s, %s, %s] [%s] [%s] [%d]", connStat[i].name, !h.m_direction ? "C2S" : "S2C", !h.m_flag ? "SUCCESS" : "FAIL", !h.m_recipient ? "PUB" : "PRIV", !h.m_trace ? "SIGN" : "ANNON", com2str(h.m_command), h.m_name, h.m_size); if (h.m_size > 0) Log("\tData: [%s]", buf[i] + HEADER_LEN); } int main(int argc, char **argv) { if (argc != 2) { Log("Usage: %s reset", argv[0]); Log("Usage: %s [server Port]", argv[0]); return -1; } if (strcmp("reset", argv[1]) == 0) { Log("Resetting server databases"); clearDatabase(USER_DATABASE); clearDatabase(FILE_DATABASE); int status; pid_t cpid; if ((cpid = fork()) == -1) { perror("fork"); return 1; } if (cpid == 0) { // Child process executes "rm -rf users" char command[40] = "rm -rf files"; // only way this worked char *args[5] = {NULL}; args[0] = strtok(command, " "); for (int i = 1; i < 3; i++) args[i] = strtok(NULL, " "); execvp(args[0], args); return 0; } else { // Parent process waits for "rm -rf users" to finish pid_t rpid; int status; if ((rpid = wait(NULL)) == -1) perror("wait"); else { Log("Clearing file storage"); if (mkdir(FILES_DIR, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { perror("mkdir"); return -1; } } return 0; } } // Make files directory if it doesn't exist DIR *pDir; struct stat s; if (stat("files", &s) != 0) { // The files directory doesn't exist if (mkdir("files", S_IRWXU | S_IRWXG | S_IRWXO) != 0) { perror("mkdir"); return -1; } } int port = atoi(argv[1]); DoServer(port); return 0; }