1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-05-31 22:18:03 +02:00
gnupg/tools/watchgnupg.c
Werner Koch 096e7457ec Change all quotes in strings and comments to the new GNU standard.
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.
2012-06-05 19:29:22 +02:00

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:
*/