/*
- * $Id$
- *
* Command-line utility to transmit a server command.
*
+ * Copyright (c) 1987-2012 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
*/
-#include "ctdl_module.h"
-
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-
#include <signal.h>
#include <errno.h>
#include <limits.h>
-#include <libcitadel.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include "citadel.h"
-#include "citadel_ipc.h"
-#include "server.h"
-#include "config.h"
+#include "include/citadel_dirs.h"
+#include <libcitadel.h>
-#define LOCKFILE "/tmp/LCK.sendcommand"
-static CtdlIPC *ipc = NULL;
+int serv_sock = (-1);
-/*
- * make sure only one copy of sendcommand runs at a time, using lock files
- */
-int set_lockfile(void)
+
+int uds_connectsock(char *sockpath)
{
- FILE *lfp;
- int onppid;
- int rv;
-
- if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
- rv = fscanf(lfp, "%d", &onppid);
- fclose(lfp);
- if (!kill(onppid, 0) || errno == EPERM)
- return 1;
+ 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);
}
- lfp = fopen(LOCKFILE, "w");
- fprintf(lfp, "%ld\n", (long) getpid());
- fclose(lfp);
- return (0);
-}
-void remove_lockfile(void)
-{
- unlink(LOCKFILE);
+ 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;
}
+
/*
- * Why both cleanup() and nq_cleanup() ? Notice the alarm() call in
- * cleanup() . If for some reason sendcommand hangs waiting for the server
- * to clean up, the alarm clock goes off and the program exits anyway.
- * The cleanup() routine makes a check to ensure it's not reentering, in
- * case the ipc module looped it somehow.
+ * input binary data from socket
*/
-void nq_cleanup(int e)
+void serv_read(char *buf, int bytes)
{
- if (e == SIGALRM)
- fprintf(stderr, "\nWatch dog time out.\n");
- remove_lockfile();
- exit(e);
+ 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(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+void serv_write(char *buf, int nbytes)
{
- unsigned int bytes_written = 0;
+ int bytes_written = 0;
int retval;
-/*
-#if defined(HAVE_OPENSSL)
- if (ipc->ssl) {
- serv_write_ssl(ipc, buf, nbytes);
- return;
- }
-#endif
-*/
while (bytes_written < nbytes) {
- retval = write(ipc->sock, &buf[bytes_written],
- nbytes - bytes_written);
+ retval = write(serv_sock, &buf[bytes_written], nbytes - bytes_written);
if (retval < 1) {
- connection_died(ipc, 0);
return;
}
- bytes_written += retval;
+ bytes_written = bytes_written + retval;
}
}
-void cleanup(int e)
-{
- static int nested = 0;
-
- alarm(30);
- signal(SIGALRM, nq_cleanup);
- serv_write(ipc, "\n", 1);
- if (nested++ < 1)
- CtdlIPCQuit(ipc);
- nq_cleanup(e);
-}
/*
- * This is implemented as a function rather than as a macro because the
- * client-side IPC modules expect logoff() to be defined. They call logoff()
- * when a problem connecting or staying connected to the server occurs.
+ * input string from socket - implemented in terms of serv_read()
*/
-void logoff(int e)
+void serv_gets(char *buf)
{
- cleanup(e);
-}
+ int i;
-/*
- * Connect sendcommand to the Citadel server running on this computer.
- */
-void np_attach_to_server(char *host, char *port)
-{
- char buf[SIZ];
- char hostbuf[256], portbuf[256];
- char *args[] =
- {"sendcommand", NULL};
- int r;
-
- fprintf(stderr, "Attaching to server...\n");
- strcpy(hostbuf, host);
- strcpy(portbuf, port);
- ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
- if (!ipc) {
- fprintf(stderr, "Can't connect: %s\n", strerror(errno));
- exit(3);
+ /* Read one character at a time.
+ */
+ for (i = 0;; i++) {
+ serv_read(&buf[i], 1);
+ if (buf[i] == '\n' || i == (SIZ-1))
+ break;
}
- CtdlIPC_chat_recv(ipc, buf);
- fprintf(stderr, "%s\n", &buf[4]);
- snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
- r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
- fprintf(stderr, "%s\n", buf);
- if (r / 100 != 2) {
- cleanup(2);
+
+ /* 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;
}
-void sendcommand_die(void) {
- exit(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);
}
+
/*
- * main
+ * Main loop. Do things and have fun.
*/
int main(int argc, char **argv)
{
int a;
- char cmd[SIZ];
- char buf[SIZ];
int watchdog = 60;
-
+ char buf[SIZ];
+ int xfermode = 0;
int relh=0;
int home=0;
char relhome[PATH_MAX]="";
char ctdldir[PATH_MAX]=CTDLDIR;
- fd_set read_fd;
- struct timeval tv;
- int ret, err;
- int server_shutting_down = 0;
-
- strcpy(ctdl_home_directory, DEFAULT_PORT);
- strcpy(cmd, "");
- /*
- * Change directories if specified
- */
- for (a = 1; a < argc; ++a) {
- if (!strncmp(argv[a], "-h", 2)) {
- relh=argv[a][2]!='/';
- if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
- sizeof ctdl_home_directory);
- else
- safestrncpy(relhome, &argv[a][2],
- sizeof relhome);
- home=1;
- } else if (!strncmp(argv[a], "-w", 2)) {
- watchdog = atoi(&argv[a][2]);
- if (watchdog<1)
- watchdog=1;
- } else {
- if (!IsEmptyStr(cmd))
- strcat(cmd, " ");
- strcat(cmd, argv[a]);
+ StartLibCitadel(SIZ);
+
+ /* Parse command line */
+ while ((a = getopt(argc, argv, "h:w:")) != EOF) {
+ switch (a) {
+ case 'h':
+ relh=optarg[0]!='/';
+ if (!relh) {
+ strncpy(ctdl_home_directory, optarg, sizeof ctdl_home_directory);
+ } else {
+ strncpy(relhome, optarg, sizeof relhome);
+ }
+ home = 1;
+ break;
+ case 'w':
+ watchdog = atoi(optarg);
+ break;
+ default:
+ fprintf(stderr, "sendcommand: usage: sendcommand [-h server_dir] [-w watchdog_timeout]\n");
+ return(1);
}
}
calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
- get_config();
- signal(SIGINT, cleanup);
- signal(SIGQUIT, cleanup);
- signal(SIGHUP, cleanup);
- signal(SIGTERM, cleanup);
-
- fprintf(stderr, "sendcommand: started (pid=%d) "
- "running in %s\n",
- (int) getpid(),
- ctdl_home_directory);
+ fprintf(stderr, "sendcommand: started (pid=%d) connecting to Citadel server at %s\n",
+ (int) getpid(),
+ file_citadel_admin_socket
+ );
fflush(stderr);
alarm(watchdog);
- signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
-
- np_attach_to_server(UDS, ctdl_home_directory);
- fflush(stderr);
- setIPCDeathHook(sendcommand_die);
+ serv_sock = uds_connectsock(file_citadel_admin_socket);
- fprintf(stderr, "%s\n", cmd);
- CtdlIPC_chat_send(ipc, cmd);
- CtdlIPC_chat_recv(ipc, buf);
+ serv_gets(buf);
fprintf(stderr, "%s\n", buf);
- tv.tv_sec = 0;
- tv.tv_usec = 1000;
-
- if (!strncasecmp(&buf[1], "31", 2)) {
- server_shutting_down = 1;
+ strcpy(buf, "");
+ for (a=optind; a<argc; ++a) {
+ if (a != optind) {
+ strcat(buf, " ");
+ }
+ strcat(buf, argv[a]);
}
- if (buf[0] == '1') {
- while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
- printf("%s\n", buf);
- alarm(watchdog); /* Kick the watchdog timer */
- }
- } else if (buf[0] == '4') {
- do {
- if (fgets(buf, sizeof buf, stdin) == NULL)
- strcpy(buf, "000");
- if (!IsEmptyStr(buf))
- if (buf[strlen(buf) - 1] == '\n')
- buf[strlen(buf) - 1] = 0;
- if (!IsEmptyStr(buf))
- if (buf[strlen(buf) - 1] == '\r')
- buf[strlen(buf) - 1] = 0;
- if (strcmp(buf, "000"))
- CtdlIPC_chat_send(ipc, buf);
-
- FD_ZERO(&read_fd);
- FD_SET(ipc->sock, &read_fd);
- ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
- err = errno;
- if (err!=0)
- printf("select failed: %d", err);
-
- if (ret == -1) {
- if (!(errno == EINTR || errno == EAGAIN))
- printf("select failed: %d", err);
- return 1;
- }
+ fprintf(stderr, "%s\n", buf);
+ serv_puts(buf);
+ serv_gets(buf);
+ fprintf(stderr, "%s\n", buf);
- if (ret != 0) {
- size_t n;
- char rbuf[SIZ];
+ xfermode = buf[0];
+
+ if ((xfermode == '4') || (xfermode == '8')) { /* send text */
+ IOBuffer IOB;
+ FDIOBuffer FDIO;
+ const char *ErrStr;
+
+ memset(&IOB, 0, sizeof(0));
+ IOB.Buf = NewStrBufPlain(NULL, SIZ);
+ IOB.fd = serv_sock;
+ FDIOBufferInit(&FDIO, &IOB, fileno(stdin), -1);
+
+ while (FileSendChunked(&FDIO, &ErrStr) >= 0)
+ alarm(watchdog); /* reset the watchdog timer */
+ if (ErrStr != NULL)
+ fprintf(stderr, "Error while piping stuff: %s\n", ErrStr);
+ FDIOBufferDelete(&FDIO);
+ FreeStrBuf(&IOB.Buf);
+ serv_puts("000");
+ }
- rbuf[0] = '\0';
- n = read(ipc->sock, rbuf, SIZ);
- if (n>0) {
- rbuf[n]='\0';
- fprintf (stderr, rbuf);
- fflush (stdout);
+ if ((xfermode == '1') || (xfermode == '8')) { /* receive text */
+ IOBuffer IOB;
+ StrBuf *Line, *OutBuf;
+ int Finished = 0;
+
+ memset(&IOB, 0, sizeof(IOB));
+ IOB.Buf = NewStrBufPlain(NULL, SIZ);
+ IOB.fd = serv_sock;
+ Line = NewStrBufPlain(NULL, SIZ);
+ OutBuf = NewStrBufPlain(NULL, SIZ * 10);
+
+ while (!Finished && (StrBuf_read_one_chunk_callback (serv_sock, 0, &IOB) >= 0))
+ {
+ eReadState State;
+
+ State = eReadSuccess;
+ while (!Finished && (State == eReadSuccess))
+ {
+ if (IOBufferStrLength(&IOB) == 0)
+ {
+ State = eMustReadMore;
+ break;
+ }
+ State = StrBufChunkSipLine(Line, &IOB);
+ switch (State)
+ {
+ case eReadSuccess:
+ if (!strcmp(ChrPtr(Line), "000"))
+ {
+ Finished = 1;
+ break;
+ }
+ StrBufAppendBuf(OutBuf, Line, 0);
+ StrBufAppendBufPlain(OutBuf, HKEY("\n"), 0);
+ alarm(watchdog); /* reset the watchdog timer */
+ break;
+ case eBufferNotEmpty:
+ break;
+ case eMustReadMore:
+ continue;
+ case eReadFail:
+ fprintf(stderr, "WTF? Exit!\n");
+ exit(-1);
+ break;
+ }
+ if (StrLength(OutBuf) > 5*SIZ)
+ {
+ fwrite(ChrPtr(OutBuf), 1, StrLength(OutBuf), stdout);
+ FlushStrBuf(OutBuf);
}
}
- alarm(watchdog); /* Kick the watchdog timer */
- } while (strcmp(buf, "000"));
- CtdlIPC_chat_send(ipc, "\n");
- CtdlIPC_chat_send(ipc, "000");
+ }
+ if (StrLength(OutBuf) > 0)
+ {
+ fwrite(ChrPtr(OutBuf), 1, StrLength(OutBuf), stdout);
+ }
+ FreeStrBuf(&Line);
+ FreeStrBuf(&OutBuf);
+ FreeStrBuf(&IOB.Buf);
}
- alarm(0); /* Shutdown the watchdog timer */
- fprintf(stderr, "sendcommand: processing ended.\n");
-
- /* Clean up and log off ... unless the server indicated that the command
- * we sent is shutting it down, in which case we want to just cut the
- * connection and exit.
- */
- if (server_shutting_down) {
- nq_cleanup(0);
+
+ if (xfermode == '6') { /* receive binary */
+ size_t len = atoi(&buf[4]);
+ size_t bytes_remaining = len;
+
+ while (bytes_remaining > 0) {
+ size_t this_block = bytes_remaining;
+ if (this_block > SIZ) this_block = SIZ;
+ serv_read(buf, this_block);
+ fwrite(buf, this_block, 1, stdout);
+ bytes_remaining -= this_block;
+ }
}
- else {
- cleanup(0);
+
+ close(serv_sock);
+ alarm(0); /* cancel the watchdog timer */
+ fprintf(stderr, "sendcommand: processing ended.\n");
+ if (xfermode == '5') {
+ return(1);
}
- return 0;
+ return(0);
}
-/*
- * Stub function
- */
-void stty_ctdl(int cmd) {
-}
+
+
+
+
+
+
+
+
+
+
+