loadtest.c: added
authorArt Cancro <ajc@citadel.org>
Sun, 27 Aug 2023 00:09:59 +0000 (20:09 -0400)
committerArt Cancro <ajc@citadel.org>
Sun, 27 Aug 2023 00:09:59 +0000 (20:09 -0400)
citadel/Makefile
citadel/utils/loadtest.c [new file with mode: 0644]

index 2241feb9ddbe6c42224dd7c04f1557ffb784c63b..026643ee500bac3bc46535bbf98d32ac110a304c 100644 (file)
@@ -10,7 +10,7 @@
 # config.mk is generated by ./configure
 include config.mk
 
-all := dbnothing ctdldump ctdlload citserver setup sendcommand citmail chkpw chkpwd
+all := dbnothing ctdldump ctdlload citserver setup sendcommand citmail chkpw chkpwd loadtest
 all: $(all)
 
 SERVER_SOURCES := $(wildcard server/*.c server/modules/*/*.c)
@@ -44,6 +44,9 @@ ctdlmigrate: utils/ctdlmigrate.c server/citadel_dirs.c utils/*.h server/*.h
 sendcommand: utils/sendcommand.c server/citadel_dirs.c utils/*.h server/*.h
        cc ${CFLAGS} ${LDFLAGS} utils/sendcommand.c -lcitadel -o sendcommand
 
+loadtest: utils/loadtest.c server/citadel_dirs.c utils/*.h server/*.h
+       cc ${CFLAGS} ${LDFLAGS} utils/loadtest.c -lcitadel -o loadtest
+
 citmail: utils/citmail.c server/citadel_dirs.c utils/*.h server/*.h
        cc ${CFLAGS} ${LDFLAGS} utils/citmail.c -lcitadel -o citmail
 
diff --git a/citadel/utils/loadtest.c b/citadel/utils/loadtest.c
new file mode 100644 (file)
index 0000000..bb713ea
--- /dev/null
@@ -0,0 +1,354 @@
+// unfinished load testing utility for Citadel Server
+//
+// Copyright (c) 1987-2023 by the citadel.org team
+//
+// This program is open source software.  Use, duplication, or disclosure
+// is subject to the terms of the GNU General Public License, version 3.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "../server/citadel_defs.h"
+#include "../server/server.h"
+#include "../server/citadel_dirs.h"
+#include <libcitadel.h>
+
+char *words[] = {
+       "lorem","ipsum","dolor","sit","amet","consectetuer","adipiscing","elit","integer","in","mi","a","mauris",
+       "ornare","sagittis","suspendisse","potenti","suspendisse","dapibus","dignissim","dolor","nam",
+       "sapien","tellus","tempus","et","tempus","ac","tincidunt","in","arcu","duis","dictum","proin","magna",
+       "nulla","pellentesque","non","commodo","et","iaculis","sit","amet","mi","mauris","condimentum","massa",
+       "ut","metus","donec","viverra","sapien","mattis","rutrum","tristique","lacus","eros","semper","tellus",
+       "et","molestie","nisi","sapien","eu","massa","vestibulum","ante","ipsum","primis","in","faucibus","orci",
+       "luctus","et","ultrices","posuere","cubilia","curae;","fusce","erat","tortor","mollis","ut","accumsan",
+       "ut","lacinia","gravida","libero","curabitur","massa","felis","accumsan","feugiat","convallis","sit",
+       "amet","porta","vel","neque","duis","et","ligula","non","elit","ultricies","rutrum","suspendisse",
+       "tempor","quisque","posuere","malesuada","velit","sed","pellentesque","mi","a","purus","integer",
+       "imperdiet","orci","a","eleifend","mollis","velit","nulla","iaculis","arcu","eu","rutrum","magna","quam",
+       "sed","elit","nullam","egestas","integer","interdum","purus","nec","mauris","vestibulum","ac","mi","in",
+       "nunc","suscipit","dapibus","duis","consectetuer","ipsum","et","pharetra","sollicitudin","metus",
+       "turpis","facilisis","magna","vitae","dictum","ligula","nulla","nec","mi","nunc","ante","urna","gravida",
+       "sit","amet","congue","et","accumsan","vitae","magna","praesent","luctus","nullam","in","velit",
+       "praesent","est","curabitur","turpis","class","aptent","taciti","sociosqu","ad","litora","torquent",
+       "per","conubia","nostra","per","inceptos","hymenaeos","cras","consectetuer","nibh","in","lacinia",
+       "ornare","turpis","sem","tempor","massa","sagittis","feugiat","mauris","nibh","non","tellus",
+       "phasellus","mi","fusce","enim","mauris","ultrices","turpis","eu","adipiscing","viverra","justo",
+       "libero","ullamcorper","massa","id","ultrices","velit","est","quis","tortor","quisque","condimentum",
+       "lacus","volutpat","nonummy","accumsan","est","nunc","imperdiet","magna","vulputate","aliquet","nisi",
+       "risus","at","est","aliquam","imperdiet","gravida","tortor","praesent","interdum","accumsan","ante",
+       "vivamus","est","ligula","consequat","sed","pulvinar","eu","consequat","vitae","eros","nulla","elit",
+       "nunc","congue","eget","scelerisque","a","tempor","ac","nisi","morbi","facilisis","pellentesque",
+       "habitant","morbi","tristique","senectus","et","netus","et","malesuada","fames","ac","turpis","egestas",
+       "in","hac","habitasse","platea","dictumst","suspendisse","vel","lorem","ut","ligula","tempor",
+       "consequat","quisque","consectetuer","nisl","eget","elit","proin","quis","mauris","ac","orci",
+       "accumsan","suscipit","sed","ipsum","sed","vel","libero","nec","elit","feugiat","blandit","vestibulum",
+       "purus","nulla","accumsan","et","volutpat","at","pellentesque","vel","urna","suspendisse","nonummy",
+       "aliquam","pulvinar","libero","donec","vulputate","orci","ornare","bibendum","condimentum","lorem",
+       "elit","dignissim","sapien","ut","aliquam","nibh","augue","in","turpis","phasellus","ac","eros",
+       "praesent","luctus","lorem","a","mollis","lacinia","leo","turpis","commodo","sem","in","lacinia","mi",
+       "quam","et","quam","curabitur","a","libero","vel","tellus","mattis","imperdiet","in","congue","neque","ut",
+       "scelerisque","bibendum","libero","lacus","ullamcorper","sapien","quis","aliquet","massa","velit",
+       "vel","orci","fusce","in","nulla","quis","est","cursus","gravida","in","nibh","lorem","ipsum","dolor","sit",
+       "amet","consectetuer","adipiscing","elit","integer","fermentum","pretium","massa","morbi","feugiat",
+       "iaculis","nunc","aenean","aliquam","pretium","orci","cum","sociis","natoque","penatibus","et","magnis",
+       "dis","parturient","montes","nascetur","ridiculus","mus","vivamus","quis","tellus","vel","quam",
+       "varius","bibendum","fusce","est","metus","feugiat","at","porttitor","et","cursus","quis","pede","nam","ut",
+       "augue","nulla","posuere","phasellus","at","dolor","a","enim","cursus","vestibulum","duis","id","nisi",
+       "duis","semper","tellus","ac","nulla","vestibulum","scelerisque","lobortis","dolor","aenean","a",
+       "felis","aliquam","erat","volutpat","donec","a","magna","vitae","pede","sagittis","lacinia","cras",
+       "vestibulum","diam","ut","arcu","mauris","a","nunc","duis","sollicitudin","erat","sit","amet","turpis",
+       "proin","at","libero","eu","diam","lobortis","fermentum","nunc","lorem","turpis","imperdiet","id",
+       "gravida","eget","aliquet","sed","purus","ut","vehicula","laoreet","ante","mauris","eu","nunc","sed","sit",
+       "amet","elit","nec","ipsum","aliquam","egestas","donec","non","nibh","cras","sodales","pretium","massa",
+       "praesent","hendrerit","est","et","risus","vivamus","eget","pede","curabitur","tristique",
+       "scelerisque","dui","nullam","ullamcorper","vivamus","venenatis","velit","eget","enim","nunc","eu",
+       "nunc","eget","felis","malesuada","fermentum","quisque","magna","mauris","ligula","felis","luctus","a",
+       "aliquet","nec","vulputate","eget","magna","quisque","placerat","diam","sed","arcu","praesent",
+       "sollicitudin","aliquam","non","sapien","quisque","id","augue","class","aptent","taciti","sociosqu",
+       "ad","litora","torquent","per","conubia","nostra","per","inceptos","hymenaeos","etiam","lacus","lectus",
+       "mollis","quis","mattis","nec","commodo","facilisis","nibh","sed","sodales","sapien","ac","ante","duis",
+       "eget","lectus","in","nibh","lacinia","auctor","fusce","interdum","lectus","non","dui","integer",
+       "accumsan","quisque","quam","curabitur","scelerisque","imperdiet","nisl","suspendisse","potenti",
+       "nam","massa","leo","iaculis","sed","accumsan","id","ultrices","nec","velit","suspendisse","potenti",
+       "mauris","bibendum","turpis","ac","viverra","sollicitudin","metus","massa","interdum","orci","non",
+       "imperdiet","orci","ante","at","ipsum","etiam","eget","magna","mauris","at","tortor","eu","lectus",
+       "tempor","tincidunt","phasellus","justo","purus","pharetra","ut","ultricies","nec","consequat","vel",
+       "nisi","fusce","vitae","velit","at","libero","sollicitudin","sodales","aenean","mi","libero","ultrices",
+       "id","suscipit","vitae","dapibus","eu","metus","aenean","vestibulum","nibh","ac","massa","vivamus",
+       "vestibulum","libero","vitae","purus","in","hac","habitasse","platea","dictumst","curabitur",
+       "blandit","nunc","non","arcu","ut","nec","nibh","morbi","quis","leo","vel","magna","commodo","rhoncus",
+       "donec","congue","leo","eu","lacus","pellentesque","at","erat","id","mi","consequat","congue","praesent",
+       "a","nisl","ut","diam","interdum","molestie","fusce","suscipit","rhoncus","sem","donec","pretium",
+       "aliquam","molestie","vivamus","et","justo","at","augue","aliquet","dapibus","pellentesque","felis",
+       "morbi","semper","in","venenatis","imperdiet","neque","donec","auctor","molestie","augue","nulla","id",
+       "arcu","sit","amet","dui","lacinia","convallis","proin","tincidunt","proin","a","ante","nunc","imperdiet",
+       "augue","nullam","sit","amet","arcu","quisque","laoreet","viverra","felis","lorem","ipsum","dolor","sit",
+       "amet","consectetuer","adipiscing","elit","in","hac","habitasse","platea","dictumst","pellentesque",
+       "habitant","morbi","tristique","senectus","et","netus","et","malesuada","fames","ac","turpis","egestas",
+       "class","aptent","taciti","sociosqu","ad","litora","torquent","per","conubia","nostra","per","inceptos",
+       "hymenaeos","nullam","nibh","sapien","volutpat","ut","placerat","quis","ornare","at","lorem","class",
+       "aptent","taciti","sociosqu","ad","litora","torquent","per","conubia","nostra","per","inceptos",
+       "hymenaeos","morbi","dictum","massa","id","libero","ut","neque","phasellus","tincidunt","nibh","ut",
+       "tincidunt","lacinia","lacus","nulla","aliquam","mi","a","interdum","dui","augue","non","pede","duis",
+       "nunc","magna","vulputate","a","porta","at","tincidunt","a","nulla","praesent","facilisis",
+       "suspendisse","sodales","feugiat","purus","cras","et","justo","a","mauris","mollis","imperdiet","morbi",
+       "erat","mi","ultrices","eget","aliquam","elementum","iaculis","id","velit","in","scelerisque","enim",
+       "sit","amet","turpis","sed","aliquam","odio","nonummy","ullamcorper","mollis","lacus","nibh","tempor",
+       "dolor","sit","amet","varius","sem","neque","ac","dui","nunc","et","est","eu","massa","eleifend","mollis",
+       "mauris","aliquet","orci","quis","tellus","ut","mattis","praesent","mollis","consectetuer","quam",
+       "nulla","nulla","nunc","accumsan","nunc","sit","amet","scelerisque","porttitor","nibh","pede","lacinia",
+       "justo","tristique","mattis","purus","eros","non","velit","aenean","sagittis","commodo","erat",
+       "aliquam","id","lacus","morbi","vulputate","vestibulum","elit"
+};
+int nwords = sizeof(words) / sizeof(char *);
+
+int serv_sock = (-1);
+
+int uds_connectsock(char *sockpath) {
+       int s;
+       struct sockaddr_un addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               fprintf(stderr, "sendcommand: Can't create socket: %s\n", strerror(errno));
+               exit(3);
+       }
+
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "sendcommand: can't connect: %s\n", strerror(errno));
+               close(s);
+               exit(3);
+       }
+
+       return s;
+}
+
+
+// input binary data from socket
+void serv_read(char *buf, int bytes) {
+       int len, rlen;
+
+       len = 0;
+       while (len < bytes) {
+               rlen = read(serv_sock, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       return;
+               }
+               len = len + rlen;
+       }
+}
+
+
+// send binary to server
+void serv_write(char *buf, int nbytes) {
+       int bytes_written = 0;
+       int retval;
+       while (bytes_written < nbytes) {
+               retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
+               if (retval < 1) {
+                       return;
+               }
+               bytes_written = bytes_written + retval;
+       }
+}
+
+
+// input string from socket - implemented in terms of serv_read()
+void serv_gets(char *buf) {
+       int i;
+
+       // Read one character at a time.
+       for (i = 0;; i++) {
+               serv_read(&buf[i], 1);
+               if (buf[i] == '\n' || i == (SIZ-1))
+                       break;
+       }
+
+       // If we got a long line, discard characters until the newline.
+       if (i == (SIZ-1)) {
+               while (buf[i] != '\n') {
+                       serv_read(&buf[i], 1);
+               }
+       }
+
+       // Strip all trailing nonprintables (crlf)
+       buf[i] = 0;
+}
+
+
+// send line to server - implemented in terms of serv_write()
+void serv_puts(char *buf) {
+       serv_write(buf, strlen(buf));
+       serv_write("\n", 1);
+}
+
+
+
+char *random_rooms[] = {
+       "Load Testing Test Room One",
+       "Load Test 2: Electric Boogaloo",
+       "Three shall be the Load Testing",
+       "This Is The Fourth Load Test Room",
+       "Five Guys Load Testing and Fries"
+};
+int nrooms = sizeof(random_rooms) / sizeof(char *);
+
+char *test_user = "Load Test User";
+
+void perform_random_thing(void) {
+       int op = random() % 3;
+       char buf[SIZ];
+       int i;
+       int bigness;
+
+       // Random operation 0 : change rooms
+       if (op == 0) {
+               snprintf(buf, sizeof(buf), "GOTO %s", random_rooms[random() % nrooms]);
+               serv_puts(buf);
+               serv_gets(buf);
+       }
+
+       // Random operation 1 : post a message
+       if (op == 1) {
+               serv_puts("ENT0 1");
+               serv_gets(buf);
+               if (buf[0] == '4') {
+
+                       bigness = random() % 500;
+                       strcpy(buf, "");
+                       for (i=0; i<bigness; ++i) {
+                               strcat(buf, words[random() % nwords]);
+                               if ( (i != 0) && ((i % 10) == 0) ) {
+                                       serv_puts(buf);
+                                       strcpy(buf, "");
+                               }
+                               else {
+                                       strcat(buf, " ");
+                               }
+                       }
+                       serv_puts(buf);
+                       serv_puts("000");
+               }
+       }
+
+       // Random operation 2 : delete a message
+       int total_msgs;
+       long selected_msg;
+       if (op == 2) {
+               total_msgs = 0;
+               selected_msg = 0;
+
+               serv_puts("MSGS ALL");
+               serv_gets(buf);
+               if (buf[0] == '1') {
+                       while (serv_gets(buf), strcmp(buf, "000")) {
+                               ++total_msgs;
+                               if ((random() % total_msgs) == 0) {
+                                       selected_msg = atol(buf);
+                               }
+                       }
+               }
+               snprintf(buf, sizeof buf, "DELE %ld", selected_msg);
+               serv_puts(buf);
+               serv_gets(buf);
+       }
+
+}
+
+
+void do_stuff(void) {
+       int i;
+       char buf[SIZ];
+
+       printf("Creating test user '%s'\n", test_user);
+       snprintf(buf, sizeof buf, "CREU %s", test_user);
+       serv_puts(buf);
+       serv_gets(buf);
+       printf("%s\n", buf);
+       snprintf(buf, sizeof buf, "ASUP %s|640k_enough_ne1|0|||6|", test_user);
+       serv_puts(buf);
+       serv_gets(buf);
+       printf("%s\n", buf);
+       snprintf(buf, sizeof buf, "USER %s", test_user);
+       serv_puts(buf);
+       serv_gets(buf);
+       printf("%s\n", buf);
+       snprintf(buf, sizeof buf, "PASS 640k_enough_ne1");
+       serv_puts(buf);
+       serv_gets(buf);
+       printf("%s\n", buf);
+
+       for (i=0; i<nrooms; ++i) {
+               printf("Creating test room '%s'\n", random_rooms[i]);
+               snprintf(buf, sizeof buf, "CRE8 1|%s|", random_rooms[i]);
+               serv_puts(buf);
+               serv_gets(buf);
+               printf("%s\n", buf);
+       }
+
+       snprintf(buf, sizeof(buf), "GOTO %s", random_rooms[0]);
+       serv_puts(buf);
+       serv_gets(buf);
+       printf("%s\n", buf);
+
+       while(1) {
+               perform_random_thing();
+       }
+}
+
+
+
+// Main loop.  Do things and have fun.
+int main(int argc, char **argv) {
+       int a;
+       char buf[SIZ];
+       char ctdldir[PATH_MAX]=CTDLDIR;
+
+       // Parse command line
+       while ((a = getopt(argc, argv, "h:")) != EOF) {
+               switch (a) {
+               case 'h':
+                       strncpy(ctdldir, optarg, sizeof ctdldir);
+                       break;
+               default:
+                       fprintf(stderr, "sendcommand: usage: %s [-h server_dir]\n", argv[0]);
+                       return(1);
+               }
+       }
+
+       fprintf(stderr, "sendcommand: started (pid=%d) connecting to Citadel server with data directory %s\n",
+               (int) getpid(),
+               ctdldir
+       );
+       fflush(stderr);
+
+       if (chdir(ctdldir) != 0) {
+               fprintf(stderr, "sendcommand: %s: %s\n", ctdldir, strerror(errno));
+               exit(errno);
+       }
+
+       serv_sock = uds_connectsock(file_citadel_admin_socket);
+       serv_gets(buf);
+       fprintf(stderr, "%s\n", buf);
+
+       do_stuff();
+
+       close(serv_sock);
+
+       return(0);
+}