mirror of
git://git.gnupg.org/gnupg.git
synced 2025-03-03 21:21:08 +01:00

The asymmetric quotes used by GNU in the past (`...') don't render nicely on modern systems. We now use two \x27 characters ('...'). The proper solution would be to use the correct Unicode symmetric quotes here. However this has the disadvantage that the system requires Unicode support. We don't want that today. If Unicode is available a generated po file can be used to output proper quotes. A simple sed script like the one used for en@quote is sufficient to change them. The changes have been done by applying sed -i "s/\`\([^'\`]*\)'/'\1'/g" to most files and fixing obvious problems by hand. The msgid strings in the po files were fixed with a similar command.
517 lines
13 KiB
C
517 lines
13 KiB
C
/* watchgnupg.c - Socket server for GnuPG logs
|
|
* Copyright (C) 2003, 2004, 2010 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
|
|
#define PGM "watchgnupg"
|
|
|
|
/* Allow for a standalone build on most systems. */
|
|
#ifdef VERSION
|
|
#define MYVERSION_LINE PGM " (GnuPG) " VERSION
|
|
#define BUGREPORT_LINE "\nReport bugs to <bug-gnupg@gnu.org>.\n"
|
|
#else
|
|
#define MYVERSION_LINE PGM " (standalone build) " __DATE__
|
|
#define BUGREPORT_LINE ""
|
|
#endif
|
|
#if !defined(SUN_LEN) || !defined(PF_LOCAL) || !defined(AF_LOCAL)
|
|
#define JNLIB_NEED_AFLOCAL
|
|
#include "../jnlib/mischelp.h"
|
|
#endif
|
|
|
|
|
|
static int verbose;
|
|
static int time_only;
|
|
|
|
static void
|
|
die (const char *format, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
|
|
fflush (stdout);
|
|
fprintf (stderr, "%s: ", PGM);
|
|
|
|
va_start (arg_ptr, format);
|
|
vfprintf (stderr, format, arg_ptr);
|
|
va_end (arg_ptr);
|
|
putc ('\n', stderr);
|
|
|
|
exit (1);
|
|
}
|
|
|
|
|
|
static void
|
|
err (const char *format, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
|
|
fflush (stdout);
|
|
fprintf (stderr, "%s: ", PGM);
|
|
|
|
va_start (arg_ptr, format);
|
|
vfprintf (stderr, format, arg_ptr);
|
|
va_end (arg_ptr);
|
|
putc ('\n', stderr);
|
|
}
|
|
|
|
static void *
|
|
xmalloc (size_t n)
|
|
{
|
|
void *p = malloc (n);
|
|
if (!p)
|
|
die ("out of core");
|
|
return p;
|
|
}
|
|
|
|
static void *
|
|
xcalloc (size_t n, size_t m)
|
|
{
|
|
void *p = calloc (n, m);
|
|
if (!p)
|
|
die ("out of core");
|
|
return p;
|
|
}
|
|
|
|
static void *
|
|
xrealloc (void *old, size_t n)
|
|
{
|
|
void *p = realloc (old, n);
|
|
if (!p)
|
|
die ("out of core");
|
|
return p;
|
|
}
|
|
|
|
|
|
struct client_s {
|
|
struct client_s *next;
|
|
int fd;
|
|
size_t size; /* Allocated size of buffer. */
|
|
size_t len; /* Current length of buffer. */
|
|
unsigned char *buffer; /* Buffer to with data already read. */
|
|
|
|
};
|
|
typedef struct client_s *client_t;
|
|
|
|
/* The list of all connected peers. */
|
|
static client_t client_list;
|
|
|
|
|
|
|
|
|
|
static void
|
|
print_fd_and_time (int fd)
|
|
{
|
|
struct tm *tp;
|
|
time_t atime = time (NULL);
|
|
|
|
tp = localtime (&atime);
|
|
if (time_only)
|
|
printf ("%3d - %02d:%02d:%02d ",
|
|
fd,
|
|
tp->tm_hour, tp->tm_min, tp->tm_sec );
|
|
else
|
|
printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
|
|
fd,
|
|
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
|
|
tp->tm_hour, tp->tm_min, tp->tm_sec );
|
|
}
|
|
|
|
|
|
/* Print LINE for the client identified by C. Calling this function
|
|
witgh LINE set to NULL, will flush the internal buffer. */
|
|
static void
|
|
print_line (client_t c, const char *line)
|
|
{
|
|
const char *s;
|
|
size_t n;
|
|
|
|
if (!line)
|
|
{
|
|
if (c->buffer && c->len)
|
|
{
|
|
print_fd_and_time (c->fd);
|
|
fwrite (c->buffer, c->len, 1, stdout);
|
|
putc ('\n', stdout);
|
|
c->len = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
while ((s = strchr (line, '\n')))
|
|
{
|
|
print_fd_and_time (c->fd);
|
|
if (c->buffer && c->len)
|
|
{
|
|
fwrite (c->buffer, c->len, 1, stdout);
|
|
c->len = 0;
|
|
}
|
|
fwrite (line, s - line + 1, 1, stdout);
|
|
line = s + 1;
|
|
}
|
|
n = strlen (line);
|
|
if (n)
|
|
{
|
|
if (c->len + n >= c->size)
|
|
{
|
|
c->size += ((n + 255) & ~255);
|
|
c->buffer = (c->buffer
|
|
? xrealloc (c->buffer, c->size)
|
|
: xmalloc (c->size));
|
|
}
|
|
memcpy (c->buffer + c->len, line, n);
|
|
c->len += n;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
setup_client (int server_fd, int is_un)
|
|
{
|
|
struct sockaddr_un addr_un;
|
|
struct sockaddr_in addr_in;
|
|
struct sockaddr *addr;
|
|
socklen_t addrlen;
|
|
int fd;
|
|
client_t client;
|
|
|
|
if (is_un)
|
|
{
|
|
addr = (struct sockaddr *)&addr_un;
|
|
addrlen = sizeof addr_un;
|
|
}
|
|
else
|
|
{
|
|
addr = (struct sockaddr *)&addr_in;
|
|
addrlen = sizeof addr_in;
|
|
}
|
|
|
|
fd = accept (server_fd, addr, &addrlen);
|
|
if (fd == -1)
|
|
{
|
|
printf ("[accepting %s connection failed: %s]\n",
|
|
is_un? "local":"tcp", strerror (errno));
|
|
}
|
|
else if (fd >= FD_SETSIZE)
|
|
{
|
|
close (fd);
|
|
printf ("[connection request denied: too many connections]\n");
|
|
}
|
|
else
|
|
{
|
|
for (client = client_list; client && client->fd != -1;
|
|
client = client->next)
|
|
;
|
|
if (!client)
|
|
{
|
|
client = xcalloc (1, sizeof *client);
|
|
client->next = client_list;
|
|
client_list = client;
|
|
}
|
|
client->fd = fd;
|
|
printf ("[client at fd %d connected (%s)]\n",
|
|
client->fd, is_un? "local":"tcp");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_version (int with_help)
|
|
{
|
|
fputs (MYVERSION_LINE "\n"
|
|
"Copyright (C) 2010 Free Software Foundation, Inc.\n"
|
|
"License GPLv3+: "
|
|
"GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
|
|
"This is free software: you are free to change and redistribute it.\n"
|
|
"There is NO WARRANTY, to the extent permitted by law.\n",
|
|
stdout);
|
|
if (with_help)
|
|
fputs
|
|
("\n"
|
|
"Usage: " PGM " [OPTIONS] SOCKETNAME\n"
|
|
" " PGM " [OPTIONS] PORT [SOCKETNAME]\n"
|
|
"Open the local socket SOCKETNAME (or the TCP port PORT)\n"
|
|
"and display log messages\n"
|
|
"\n"
|
|
" --tcp listen on a TCP port and optionally on a local socket\n"
|
|
" --force delete an already existing socket file\n"
|
|
" --verbose enable extra informational output\n"
|
|
" --time-only print only the time; not a full timestamp\n"
|
|
" --version print version of the program and exit\n"
|
|
" --help display this help and exit\n"
|
|
BUGREPORT_LINE, stdout );
|
|
|
|
exit (0);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
int last_argc = -1;
|
|
int force = 0;
|
|
int tcp = 0;
|
|
|
|
struct sockaddr_un srvr_addr_un;
|
|
struct sockaddr_in srvr_addr_in;
|
|
struct sockaddr *addr_in = NULL;
|
|
struct sockaddr *addr_un = NULL;
|
|
socklen_t addrlen_in, addrlen_un;
|
|
unsigned short port;
|
|
int server_un, server_in;
|
|
int flags;
|
|
|
|
if (argc)
|
|
{
|
|
argc--; argv++;
|
|
}
|
|
while (argc && last_argc != argc )
|
|
{
|
|
last_argc = argc;
|
|
if (!strcmp (*argv, "--"))
|
|
{
|
|
argc--; argv++;
|
|
break;
|
|
}
|
|
else if (!strcmp (*argv, "--version"))
|
|
print_version (0);
|
|
else if (!strcmp (*argv, "--help"))
|
|
print_version (1);
|
|
else if (!strcmp (*argv, "--verbose"))
|
|
{
|
|
verbose = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--time-only"))
|
|
{
|
|
time_only = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--force"))
|
|
{
|
|
force = 1;
|
|
argc--; argv++;
|
|
}
|
|
else if (!strcmp (*argv, "--tcp"))
|
|
{
|
|
tcp = 1;
|
|
argc--; argv++;
|
|
}
|
|
}
|
|
|
|
if (!((!tcp && argc == 1) || (tcp && (argc == 1 || argc == 2))))
|
|
{
|
|
fprintf (stderr, "usage: " PGM " socketname\n"
|
|
" " PGM " --tcp port [socketname]\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (tcp)
|
|
{
|
|
port = atoi (*argv);
|
|
argc--; argv++;
|
|
}
|
|
else
|
|
{
|
|
port = 0;
|
|
}
|
|
|
|
setvbuf (stdout, NULL, _IOLBF, 0);
|
|
|
|
if (tcp)
|
|
{
|
|
int i = 1;
|
|
server_in = socket (PF_INET, SOCK_STREAM, 0);
|
|
if (server_in == -1)
|
|
die ("socket(PF_INET) failed: %s\n", strerror (errno));
|
|
if (setsockopt (server_in, SOL_SOCKET, SO_REUSEADDR,
|
|
(unsigned char *)&i, sizeof (i)))
|
|
err ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
|
|
if (verbose)
|
|
fprintf (stderr, "listening on port %hu\n", port);
|
|
}
|
|
else
|
|
server_in = -1;
|
|
|
|
if (argc)
|
|
{
|
|
server_un = socket (PF_LOCAL, SOCK_STREAM, 0);
|
|
if (server_un == -1)
|
|
die ("socket(PF_LOCAL) failed: %s\n", strerror (errno));
|
|
if (verbose)
|
|
fprintf (stderr, "listening on socket '%s'\n", *argv);
|
|
}
|
|
else
|
|
server_un = -1;
|
|
|
|
/* We better set the listening socket to non-blocking so that we
|
|
don't get bitten by race conditions in accept. The should not
|
|
happen for Unix Domain sockets but well, shit happens. */
|
|
if (server_in != -1)
|
|
{
|
|
flags = fcntl (server_in, F_GETFL, 0);
|
|
if (flags == -1)
|
|
die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
|
|
if ( fcntl (server_in, F_SETFL, (flags | O_NONBLOCK)) == -1)
|
|
die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
|
|
}
|
|
if (server_un != -1)
|
|
{
|
|
flags = fcntl (server_un, F_GETFL, 0);
|
|
if (flags == -1)
|
|
die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
|
|
if ( fcntl (server_un, F_SETFL, (flags | O_NONBLOCK)) == -1)
|
|
die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
|
|
}
|
|
|
|
if (tcp)
|
|
{
|
|
memset (&srvr_addr_in, 0, sizeof srvr_addr_in);
|
|
srvr_addr_in.sin_family = AF_INET;
|
|
srvr_addr_in.sin_port = htons (port);
|
|
srvr_addr_in.sin_addr.s_addr = htonl (INADDR_ANY);
|
|
addr_in = (struct sockaddr *)&srvr_addr_in;
|
|
addrlen_in = sizeof srvr_addr_in;
|
|
}
|
|
if (argc)
|
|
{
|
|
memset (&srvr_addr_un, 0, sizeof srvr_addr_un);
|
|
srvr_addr_un.sun_family = AF_LOCAL;
|
|
strncpy (srvr_addr_un.sun_path, *argv, sizeof (srvr_addr_un.sun_path)-1);
|
|
srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0;
|
|
addr_un = (struct sockaddr *)&srvr_addr_un;
|
|
addrlen_un = SUN_LEN (&srvr_addr_un);
|
|
}
|
|
else
|
|
addrlen_un = 0; /* Silent gcc. */
|
|
|
|
if (server_in != -1 && bind (server_in, addr_in, addrlen_in))
|
|
die ("bind to port %hu failed: %s\n", port, strerror (errno));
|
|
|
|
again:
|
|
if (server_un != -1 && bind (server_un, addr_un, addrlen_un))
|
|
{
|
|
if (errno == EADDRINUSE && force)
|
|
{
|
|
force = 0;
|
|
remove (srvr_addr_un.sun_path);
|
|
goto again;
|
|
}
|
|
else
|
|
die ("bind to '%s' failed: %s\n", *argv, strerror (errno));
|
|
}
|
|
|
|
if (server_in != -1 && listen (server_in, 5))
|
|
die ("listen on inet failed: %s\n", strerror (errno));
|
|
if (server_un != -1 && listen (server_un, 5))
|
|
die ("listen on local failed: %s\n", strerror (errno));
|
|
|
|
for (;;)
|
|
{
|
|
fd_set rfds;
|
|
int max_fd;
|
|
client_t client;
|
|
|
|
/* Usually we don't have that many connections, thus it is okay
|
|
to set them allways from scratch and don't maintain an active
|
|
fd_set. */
|
|
FD_ZERO (&rfds);
|
|
max_fd = -1;
|
|
if (server_in != -1)
|
|
{
|
|
FD_SET (server_in, &rfds);
|
|
max_fd = server_in;
|
|
}
|
|
if (server_un != -1)
|
|
{
|
|
FD_SET (server_un, &rfds);
|
|
if (server_un > max_fd)
|
|
max_fd = server_un;
|
|
}
|
|
for (client = client_list; client; client = client->next)
|
|
if (client->fd != -1)
|
|
{
|
|
FD_SET (client->fd, &rfds);
|
|
if (client->fd > max_fd)
|
|
max_fd = client->fd;
|
|
}
|
|
|
|
if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
|
|
continue; /* Ignore any errors. */
|
|
|
|
if (server_in != -1 && FD_ISSET (server_in, &rfds))
|
|
setup_client (server_in, 0);
|
|
if (server_un != -1 && FD_ISSET (server_un, &rfds))
|
|
setup_client (server_un, 1);
|
|
|
|
for (client = client_list; client; client = client->next)
|
|
if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
|
|
{
|
|
char line[256];
|
|
int n;
|
|
|
|
n = read (client->fd, line, sizeof line - 1);
|
|
if (n < 0)
|
|
{
|
|
int save_errno = errno;
|
|
print_line (client, NULL); /* flush */
|
|
printf ("[client at fd %d read error: %s]\n",
|
|
client->fd, strerror (save_errno));
|
|
close (client->fd);
|
|
client->fd = -1;
|
|
}
|
|
else if (!n)
|
|
{
|
|
print_line (client, NULL); /* flush */
|
|
close (client->fd);
|
|
printf ("[client at fd %d disconnected]\n", client->fd);
|
|
client->fd = -1;
|
|
}
|
|
else
|
|
{
|
|
line[n] = 0;
|
|
print_line (client, line);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Local Variables:
|
|
compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
|
|
End:
|
|
*/
|