[BACK]Return to Listener.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

Annotation of fam/fam/Listener.c++, Revision 1.1.1.1

1.1       trev        1: //  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
                      2: //
                      3: //  This program is free software; you can redistribute it and/or modify it
                      4: //  under the terms of version 2 of the GNU General Public License as
                      5: //  published by the Free Software Foundation.
                      6: //
                      7: //  This program is distributed in the hope that it would be useful, but
                      8: //  WITHOUT ANY WARRANTY; without even the implied warranty of
                      9: //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
                     10: //  license provided herein, whether implied or otherwise, is limited to
                     11: //  this program in accordance with the express provisions of the GNU
                     12: //  General Public License.  Patent licenses, if any, provided herein do not
                     13: //  apply to combinations of this program with other product or programs, or
                     14: //  any other product whatsoever.  This program is distributed without any
                     15: //  warranty that the program is delivered free of the rightful claim of any
                     16: //  third person by way of infringement or the like.  See the GNU General
                     17: //  Public License for more details.
                     18: //
                     19: //  You should have received a copy of the GNU General Public License along
                     20: //  with this program; if not, write the Free Software Foundation, Inc., 59
                     21: //  Temple Place - Suite 330, Boston MA 02111-1307, USA.
                     22:
                     23: #include "Listener.h"
                     24:
                     25: #include <assert.h>
                     26: #include <fcntl.h>
                     27: #include <stdlib.h>
                     28: #include <sys/types.h>
                     29: #include <netinet/in.h>
                     30: #include <arpa/inet.h>  // for inet_ntoa
                     31: #include <rpc/rpc.h>
                     32: #include <rpc/pmap_clnt.h>
                     33: #include <rpc/clnt.h>
                     34: #include <sys/ioctl.h>
                     35: #include <sys/socket.h>
                     36: #include <sys/stat.h>
                     37: #include <sys/un.h>
                     38: #include <unistd.h>
                     39:
                     40: #include <errno.h>
                     41:
                     42: #include "Log.h"
                     43: #include "LocalClient.h"
                     44: #include "Scheduler.h"
                     45: #include "Cred.h"
                     46: #include "BTree.h"
                     47:
                     48: #if !(HAVE_BINDRESVPORT_PROTO)
                     49: extern "C" int bindresvport(int sd, struct sockaddr_in *);
                     50: #endif
                     51:
                     52: struct NegotiatingClient
                     53: {
                     54:     NegotiatingClient(int fd, uid_t u, struct sockaddr_un *s);
                     55:     int sock;
                     56:     uid_t uid;
                     57:     struct sockaddr_un sun;
                     58: };
                     59:
                     60: BTree<int, NegotiatingClient *> negotiating_clients;
                     61:
                     62: static void cleanup_negotiation(void *closure);
                     63:
                     64: Listener::Listener(bool sbi, bool lo, unsigned long p, unsigned long v)
                     65: : program(p),
                     66:   version(v),
                     67:   rendezvous_fd(-1),
                     68:   started_by_inetd(sbi),
                     69:   _ugly_sock(-1),
                     70:   local_only(lo)
                     71: {
                     72:     if (started_by_inetd)
                     73:     {
                     74: 	//  Portmapper already knows about
                     75: 	//  us, so just wait for the requests to start rolling in.
                     76:
                     77: 	set_rendezvous_fd(RENDEZVOUS_FD);
                     78:
                     79: 	dirty_ugly_hack();
                     80:     }
                     81:     else
                     82:     {
                     83: 	//  Need to register with portmapper.
                     84: 	//  Unless we're debugging, fork and close all descriptors.
                     85:
                     86: 	int sock = socket(PF_INET, SOCK_STREAM, 0);
                     87: 	if (sock < 0)
                     88: 	{   Log::perror("can't create TCP/IP socket for rendezvous");
                     89: 	    exit(1);
                     90: 	}
                     91: 	struct sockaddr_in addr;
                     92: 	memset(&addr, 0, sizeof addr);
                     93: 	addr.sin_family = AF_INET;
                     94:         addr.sin_addr.s_addr = local_only ? htonl(INADDR_LOOPBACK) : 0;
                     95: 	addr.sin_port = htons(0);
                     96: 	if (bindresvport(sock, &addr) < 0)
                     97: 	{
                     98: 	    Log::perror("can't bind to reserved port");
                     99: 	    exit(1);
                    100: 	}
                    101: 	if (listen(sock, 1) < 0)
                    102: 	{
                    103: 	    Log::perror("can't listen for rendezvous");
                    104: 	    exit(1);
                    105: 	}
                    106: 	(void) pmap_unset(program, version);
                    107: 	if (!pmap_set(program, version, IPPROTO_TCP, ntohs(addr.sin_port)))
                    108: 	{
                    109: 	    Log::error("can't register with portmapper.");
                    110: 	    exit(1);
                    111: 	}
                    112: 	set_rendezvous_fd(sock);
                    113:     }
                    114: }
                    115:
                    116: Listener::~Listener()
                    117: {
                    118:     if (rendezvous_fd >= 0)
                    119:     {   if (!started_by_inetd)
                    120: 	    pmap_unset(program, version);
                    121: 	int rc = close(rendezvous_fd);
                    122: 	if (rc < 0)
                    123: 	    Log::perror("close rendezvous port(%d)", rendezvous_fd);
                    124: 	else
                    125: 	    Log::debug("fam service closed");
                    126: 	(void) Scheduler::remove_read_handler(rendezvous_fd);
                    127:     }
                    128:     if (_ugly_sock >= 0)
                    129:     {   (void) close(_ugly_sock);
                    130: 	(void) unlink("/tmp/.fam_socket");
                    131:     }
                    132: }
                    133:
                    134: void
                    135: Listener::set_rendezvous_fd(int newfd)
                    136: {
                    137:     if (rendezvous_fd >= 0)
                    138:     {   (void) Scheduler::remove_read_handler(rendezvous_fd);
                    139: 	(void) close(rendezvous_fd);
                    140:     }
                    141:     rendezvous_fd = newfd;
                    142:     if (rendezvous_fd >= 0)
                    143:     {   (void) Scheduler::install_read_handler(rendezvous_fd,
                    144: 					       accept_client, this);
                    145: 	Log::debug("listening for clients on descriptor %d", rendezvous_fd);
                    146:     }
                    147: }
                    148:
                    149: void
                    150: Listener::accept_client(int rendezvous_fd, void *closure)
                    151: {
                    152:     Listener *listener = (Listener *)closure;
                    153:
                    154:     // Get the new socket.
                    155:
                    156:     struct sockaddr_in addr;
                    157:     CONFIG_SOCKLEN_T addrlen = sizeof addr;
                    158:     int client_fd = accept(rendezvous_fd, (struct sockaddr *) &addr, &addrlen);
                    159:     if (client_fd < 0)
                    160:     {
                    161: 	Log::perror("failed to accept new client");
                    162: 	return;
                    163:     }
                    164:
                    165:     if (ntohl(addr.sin_addr.s_addr) == INADDR_LOOPBACK)
                    166:     {
                    167:         Log::debug("client fd %d is local/untrusted.", client_fd);
                    168: 	// Client is local.
                    169:
                    170:         Cred cred = Cred::get_cred_for_untrusted_conn(client_fd);
                    171: 	new TCP_Client(addr.sin_addr, client_fd, cred);
                    172:         // We don't need a reference to this object.  The constructor
                    173:         // takes care of registering it with the Scheduler.
                    174:     }
                    175:     else
                    176:     {
                    177:         assert(!listener->local_only);
                    178:
                    179: 	// Check that client is superuser.
                    180:         Cred cred;
                    181: 	if (ntohs(addr.sin_port) >= IPPORT_RESERVED)
                    182: 	{
                    183:             //  Client isn't connecting on a privileged port, so we will
                    184:             //  ignore who they say they are, and treat them as our
                    185:             //  untrusted-user.
                    186:             cred = Cred::get_cred_for_untrusted_conn(client_fd);
                    187: 	}
                    188:         Log::debug("client fd %d from %s is remote/%strusted",
                    189:                    client_fd, inet_ntoa(addr.sin_addr),
                    190:                    cred.is_valid() ? "un" : "");
                    191:
                    192: 	new TCP_Client(addr.sin_addr, client_fd, cred);
                    193:         // We don't need a reference to this object.  The constructor
                    194:         // takes care of registering it with the Scheduler.
                    195:     }
                    196: }
                    197:
                    198: void
                    199: Listener::create_local_client(TCP_Client &inet_client, uid_t uid)
                    200: {
                    201:     //  This TCP_Client wants to use a UNIX domain socket instead of the
                    202:     //  inet socket for communication.  Create the new socket owned by the
                    203:     //  requested user and pass the name back to the client.
                    204:
                    205:     //  Unset TMPDIR to ensure that tempnam() works as desired
                    206:     putenv("TMPDIR=");
                    207:
                    208:     char *tmpfile = tempnam("/tmp", ".fam");
                    209: #if defined(__FreeBSD__)
                    210:     sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };
                    211: #else
                    212:     sockaddr_un sun = { AF_UNIX, "" };
                    213: #endif
                    214:     if(strlen(tmpfile) >= (sizeof(sun.sun_path) - 1))
                    215:     {
                    216:         Log::error("tmpnam() too long for sun_path (%d >= %d)!",
                    217:                    strlen(tmpfile), sizeof(sun.sun_path) - 1);
                    218:         free(tmpfile);
                    219:         return;
                    220:     }
                    221:     strcpy(sun.sun_path, tmpfile);
                    222:     free(tmpfile);
                    223:
                    224:     Cred::SuperUser.become_user();
                    225:     int client_sock = socket(PF_UNIX, SOCK_STREAM, 0);
                    226:     if (client_sock < 0)
                    227:     {   Log::perror("localclient socket(PF_UNIX, SOCK_STREAM, 0)");
                    228: 	return;
                    229:     }
                    230:
                    231:     Log::debug("client %s said uid %d; creating %s",
                    232:                inet_client.name(), uid, sun.sun_path);
                    233:
                    234:     unlink(sun.sun_path);
                    235:     if (bind(client_sock, (sockaddr *) &sun, sizeof sun) != 0)
                    236:     {   Log::perror("localclient bind");
                    237: 	close(client_sock);
                    238:         return;
                    239:     }
                    240:
                    241:     if (chmod(sun.sun_path, 0600) != 0)
                    242:     {   Log::perror("localclient chmod");
                    243: 	close(client_sock);
                    244:         return;
                    245:     }
                    246:
                    247:     if (chown(sun.sun_path, uid, (gid_t)-1) != 0)
                    248:     {   Log::perror("localclient chown");
                    249: 	close(client_sock);
                    250:         return;
                    251:     }
                    252:
                    253:     //  Since we're going to start listening on this socket, set a task
                    254:     //  to clean it up if we don't receive a connection within 60 seconds.
                    255:     NegotiatingClient *nc = new NegotiatingClient(client_sock, uid, &sun);
                    256:     negotiating_clients.insert(client_sock, nc);
                    257:     timeval nto;
                    258:     gettimeofday(&nto, NULL);
                    259:     nto.tv_sec += 60;  //  XXX that should be configurable
                    260:     Scheduler::install_onetime_task(nto, cleanup_negotiation, nc);
                    261:
                    262:     if (listen(client_sock, 1) != 0)
                    263:     {   Log::perror("localclient listen");
                    264: 	close(client_sock);
                    265:         return;
                    266:     }
                    267:
                    268:     Scheduler::install_read_handler(client_sock, accept_localclient, NULL);
                    269:     Log::debug("listening for requests for uid %d on descriptor %d (%s)",
                    270:                uid, client_sock, sun.sun_path);
                    271:
                    272:     inet_client.send_sockaddr_un(sun);
                    273: }
                    274:
                    275: void
                    276: Listener::accept_localclient(int ofd, void *)
                    277: {
                    278:     NegotiatingClient *nc = negotiating_clients.find(ofd);
                    279:     assert(nc);
                    280:
                    281:     // Get the new socket.
                    282:
                    283: #if defined(__FreeBSD__)
                    284:     struct sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };
                    285: #else
                    286:     struct sockaddr_un sun = { AF_UNIX, "" };
                    287: #endif
                    288:     CONFIG_SOCKLEN_T sunlen = sizeof(sun);
                    289:     int client_fd = accept(ofd, (struct sockaddr *) &sun, &sunlen);
                    290:     if (client_fd < 0)
                    291:     {
                    292: 	Log::perror("failed to accept new client");
                    293: 	return;
                    294:     }
                    295:
                    296:     //  Keep the scheduler from helpfully cleaning this up.
                    297:     Scheduler::remove_onetime_task(cleanup_negotiation, nc);
                    298:
                    299:     Log::debug("client fd %d is local/trusted (socket %s, uid %d).",
                    300:                client_fd, nc->sun.sun_path, nc->uid);
                    301:     Cred cred(nc->uid, client_fd);
                    302:     new LocalClient(client_fd, &(nc->sun), cred);
                    303:     // We don't need a reference to this object.  The constructor
                    304:     // takes care of registering it with the Scheduler.
                    305:
                    306:     //  Stop listening for new connections on that socket.
                    307:     Scheduler::remove_read_handler(ofd);
                    308:     close(ofd);
                    309:
                    310:     //  This client is no longer negotiating, so remove it from the list
                    311:     //  and delete it.
                    312:     negotiating_clients.remove(ofd);
                    313:     delete nc;
                    314: }
                    315:
                    316: //  Here's the problem.  If the network is stopped and restarted,
                    317: //  inetd and portmapper are killed and new ones are launched in their
                    318: //  place.  Since they have no idea that fam is already running, inetd
                    319: //  opens a new TCP/IP socket and registers it with the new portmapper.
                    320: //  Meanwhile, the old fam has /dev/imon open, and any new fam launched
                    321: //  by the new inetd won't be able to use imon.  The old fam can't just
                    322: //  quit; it has all the state of all its existing clients to contend with.
                    323:
                    324: //  So here's the dirty, ugly, hack solution.  The first fam to run is
                    325: //  the master.  The master opens the UNIX domain socket,
                    326: //  /tmp/.fam_socket, and listens for connections from slaves.  Each
                    327: //  subsequent fam is a slave.  It knows it's a slave because it can
                    328: //  connect to the master.  The slave passes its rendezvous descriptor
                    329: //  (the one that fam clients will connect to) through to the master
                    330: //  using the access rights mechanism of Unix domain sockets.  The
                    331: //  master receives the new descriptor and starts accepting clients on
                    332: //  it.  Meanwhile, the slave blocks, waiting for the master to close
                    333: //  the connection on /tmp/.fam_socket.  That happens when the master
                    334: //  exits.
                    335:
                    336: //  This master/slave stuff has nothing to do with fam forwarding
                    337: //  requests to fams on other hosts.  That's called client/server
                    338: //  fams.
                    339:
                    340: //  Why not just kill fam when we kill the rest of the network
                    341: //  services?  Because too many programs need it.  The desktop, the
                    342: //  desktop sysadmin tools, MediaMail...  None of those should go down
                    343: //  when the network goes down, and none of them are designed to
                    344: //  handle fam dying.
                    345:
                    346: void
                    347: Listener::dirty_ugly_hack()
                    348: {
                    349: #if defined(__FreeBSD__)
                    350:     static sockaddr_un sun = { sizeof (sockaddr_un), AF_UNIX, "/tmp/.fam_socket" };
                    351: #else
                    352:     static sockaddr_un sun = { AF_UNIX, "/tmp/.fam_socket" };
                    353: #endif
                    354:
                    355:     int sock = socket(PF_UNIX, SOCK_STREAM, 0);
                    356:     if (sock < 0)
                    357:     {   Log::perror("socket(PF_UNIX, SOCK_STREAM, 0)");
                    358: 	exit(1);
                    359:     }
                    360:
                    361:     struct stat sb;
                    362:     if (lstat(sun.sun_path, &sb) == 0 &&
                    363: 	sb.st_uid == 0 && S_ISSOCK(sb.st_mode))
                    364:     {
                    365: 	if (connect(sock, (sockaddr *) &sun, sizeof sun) == 0)
                    366: 	{
                    367: 	    // Another fam is listening to /tmp/.fam_socket.
                    368: 	    // Pass our rendezvous fd to the other fam and
                    369: 	    // sleep until that fam exits.
                    370:
                    371: 	    int rights[1] = { rendezvous_fd };
                    372: 	    msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };
                    373: 	    if (sendmsg(sock, &msg, 0) < 0)
                    374: 	    {   Log::perror("sendmsg");
                    375: 		exit(1);
                    376: 	    }
                    377: 	    Log::debug("fam (process %d) enslaved to master fam", getpid());
                    378: 	    char data[1];
                    379: 	    int nb = read(sock, &data, sizeof data);
                    380: 	    assert(nb == 0);
                    381: 	    exit(0);
                    382: 	}
                    383: 	else
                    384: 	    Log::debug("could not enslave myself: %m");
                    385:     }
                    386:
                    387:     //  We were unable to connect to another fam.
                    388:     //	We'll create our own dirty ugly hack socket and accept connections.
                    389:
                    390:     (void) unlink(sun.sun_path);
                    391:     if (bind(sock, (sockaddr *) &sun, sizeof sun) != 0)
                    392:     {   Log::perror("bind");
                    393: 	exit(1);
                    394:     }
                    395:
                    396:     if (chmod(sun.sun_path, 0700) != 0)
                    397:     {   Log::perror("chmod");
                    398: 	exit(1);
                    399:     }
                    400:
                    401:     if (listen(sock, 1) != 0)
                    402:     {   Log::perror("listen");
                    403: 	exit(1);
                    404:     }
                    405:
                    406:     (void) Scheduler::install_read_handler(sock, accept_ugly_hack, this);
                    407:     _ugly_sock = sock;
                    408:     Log::debug("fam (process %d) is master fam.", getpid());
                    409: }
                    410:
                    411: void
                    412: Listener::accept_ugly_hack(int ugly, void *closure)
                    413: {
                    414:     Listener *listener = (Listener *) closure;
                    415:     assert(ugly == listener->_ugly_sock);
                    416:
                    417:     //  Accept a new ugly connection.
                    418:
                    419:     struct sockaddr_un sun;
                    420:     CONFIG_SOCKLEN_T sunlen = sizeof sun;
                    421:     int sock = accept(ugly, (struct sockaddr *)(&sun), &sunlen);
                    422:     if (sock < 0)
                    423:     {   Log::perror("accept");
                    424: 	return;
                    425:     }
                    426:
                    427:     (void) Scheduler::install_read_handler(sock, read_ugly_hack, listener);
                    428: }
                    429:
                    430: void
                    431: Listener::read_ugly_hack(int sock, void *closure)
                    432: {
                    433:     Listener *listener = (Listener *) closure;
                    434:
                    435:     int rights[1] = { -1 };
                    436:     msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };
                    437:     if (recvmsg(sock, &msg, 0) >= 0)
                    438:     {
                    439: 	Log::debug("master fam (process %d) has new slave", getpid());
                    440:
                    441: 	assert(rights[0] >= 0);
                    442: 	listener->set_rendezvous_fd(rights[0]);
                    443:
                    444: 	//  Turn the inactivity timeout all the way down.  We want to
                    445: 	//  clean up this dirty ugly hack as soon as possible after
                    446:         //  the last Activity is destroyed.
                    447: 	Activity::timeout(0);
                    448:
                    449: 	// Forget socket -- it will be closed on exit.
                    450: 	// Slave is blocked waiting for socket to close, so slave
                    451: 	// will exit when master exits.
                    452:
                    453: 	(void) Scheduler::remove_read_handler(sock);
                    454:     }
                    455:     else
                    456:     {	Log::perror("recvmsg");
                    457: 	(void) Scheduler::remove_read_handler(sock);
                    458: 	(void) close(sock);
                    459:     }
                    460: }
                    461:
                    462: NegotiatingClient::NegotiatingClient(int fd, uid_t u, struct sockaddr_un *sunp)
                    463:     : sock(fd), uid(u)
                    464: {
                    465:     sun.sun_family = AF_UNIX;
                    466:     strcpy(sun.sun_path, sunp->sun_path);
                    467: }
                    468:
                    469: static void
                    470: cleanup_negotiation(void *closure)
                    471: {
                    472:     NegotiatingClient *nc = (NegotiatingClient *)closure;
                    473:
                    474:     Log::debug("cleaning up incomplete negotiation for client %d", nc->sock);
                    475:
                    476:     //  Remove client from list
                    477:     negotiating_clients.remove(nc->sock);
                    478:
                    479:     //  Remove the read handler & close the socket
                    480:     Scheduler::remove_read_handler(nc->sock);
                    481:     close(nc->sock);
                    482:     nc->sock = -1;
                    483:
                    484:     //  Remove the temp file
                    485:     uid_t preveuid = geteuid();
                    486:     if (preveuid) seteuid(0);
                    487:     seteuid(nc->uid);
                    488:     unlink(nc->sun.sun_path);
                    489:     if (nc->uid) seteuid(0);
                    490:     seteuid(preveuid);
                    491:
                    492:     delete nc;
                    493: }
                    494:

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>