⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 listener.c++

📁 好东东。linux下面的文件节点、文件状态的变化监听代码
💻 C++
字号:
//  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.//  //  This program is free software; you can redistribute it and/or modify it//  under the terms of version 2 of the GNU General Public License as//  published by the Free Software Foundation.////  This program is distributed in the hope that it would be useful, but//  WITHOUT ANY WARRANTY; without even the implied warranty of//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any//  license provided herein, whether implied or otherwise, is limited to//  this program in accordance with the express provisions of the GNU//  General Public License.  Patent licenses, if any, provided herein do not//  apply to combinations of this program with other product or programs, or//  any other product whatsoever.  This program is distributed without any//  warranty that the program is delivered free of the rightful claim of any//  third person by way of infringement or the like.  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, write the Free Software Foundation, Inc., 59//  Temple Place - Suite 330, Boston MA 02111-1307, USA.#include "Listener.h"#include <assert.h>#include <fcntl.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>  // for inet_ntoa#include <rpc/rpc.h>#include <rpc/pmap_clnt.h>#include <rpc/clnt.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/un.h>#include <unistd.h>#include <errno.h>#include "Log.h"#include "LocalClient.h"#include "Scheduler.h"#include "Cred.h"#include "BTree.h"#if !(HAVE_BINDRESVPORT_PROTO)extern "C" int bindresvport(int sd, struct sockaddr_in *);#endifstruct NegotiatingClient{    NegotiatingClient(int fd, uid_t u, struct sockaddr_un *s);    int sock;    uid_t uid;    struct sockaddr_un sun;};BTree<int, NegotiatingClient *> negotiating_clients;static void cleanup_negotiation(void *closure);Listener::Listener(bool sbi, bool lo, unsigned long p, unsigned long v): program(p),  version(v),  rendezvous_fd(-1),  started_by_inetd(sbi),  _ugly_sock(-1),  local_only(lo){    if (started_by_inetd)    {	//  Portmapper already knows about	//  us, so just wait for the requests to start rolling in.	set_rendezvous_fd(RENDEZVOUS_FD);	dirty_ugly_hack();    }    else    {	//  Need to register with portmapper.	//  Unless we're debugging, fork and close all descriptors.	int sock = socket(PF_INET, SOCK_STREAM, 0);	if (sock < 0)	{   Log::perror("can't create TCP/IP socket for rendezvous");	    exit(1);	}	struct sockaddr_in addr;	memset(&addr, 0, sizeof addr);	addr.sin_family = AF_INET;        addr.sin_addr.s_addr = local_only ? htonl(INADDR_LOOPBACK) : 0;	addr.sin_port = htons(0);	if (bindresvport(sock, &addr) < 0)	{	    Log::perror("can't bind to reserved port");	    exit(1);	}	if (listen(sock, 1) < 0)	{	    Log::perror("can't listen for rendezvous");	    exit(1);	}	(void) pmap_unset(program, version);	if (!pmap_set(program, version, IPPROTO_TCP, ntohs(addr.sin_port)))	{	    Log::error("can't register with portmapper.");	    exit(1);	}	set_rendezvous_fd(sock);    }}Listener::~Listener(){    if (rendezvous_fd >= 0)    {   if (!started_by_inetd)	    pmap_unset(program, version);	int rc = close(rendezvous_fd);	if (rc < 0)	    Log::perror("close rendezvous port(%d)", rendezvous_fd);	else	    Log::debug("fam service closed");	(void) Scheduler::remove_read_handler(rendezvous_fd);    }    if (_ugly_sock >= 0)    {   (void) close(_ugly_sock);	(void) unlink("/tmp/.fam_socket");    }}voidListener::set_rendezvous_fd(int newfd){    if (rendezvous_fd >= 0)    {   (void) Scheduler::remove_read_handler(rendezvous_fd);	(void) close(rendezvous_fd);    }    rendezvous_fd = newfd;    if (rendezvous_fd >= 0)    {   (void) Scheduler::install_read_handler(rendezvous_fd,					       accept_client, this);	Log::debug("listening for clients on descriptor %d", rendezvous_fd);    }}voidListener::accept_client(int rendezvous_fd, void *closure){    Listener *listener = (Listener *)closure;    // Get the new socket.    struct sockaddr_in addr;    CONFIG_SOCKLEN_T addrlen = sizeof addr;    int client_fd = accept(rendezvous_fd, (struct sockaddr *) &addr, &addrlen);    if (client_fd < 0)    {	Log::perror("failed to accept new client");	return;    }    if (ntohl(addr.sin_addr.s_addr) == INADDR_LOOPBACK)    {        Log::debug("client fd %d is local/untrusted.", client_fd);	// Client is local.        Cred cred = Cred::get_cred_for_untrusted_conn(client_fd);	new TCP_Client(addr.sin_addr, client_fd, cred);        // We don't need a reference to this object.  The constructor        // takes care of registering it with the Scheduler.    }    else    {        assert(!listener->local_only);	// Check that client is superuser.        Cred cred;	if (ntohs(addr.sin_port) >= IPPORT_RESERVED)	{            //  Client isn't connecting on a privileged port, so we will            //  ignore who they say they are, and treat them as our            //  untrusted-user.            cred = Cred::get_cred_for_untrusted_conn(client_fd);	}        Log::debug("client fd %d from %s is remote/%strusted",                   client_fd, inet_ntoa(addr.sin_addr),                   cred.is_valid() ? "un" : "");	new TCP_Client(addr.sin_addr, client_fd, cred);        // We don't need a reference to this object.  The constructor        // takes care of registering it with the Scheduler.    }}voidListener::create_local_client(TCP_Client &inet_client, uid_t uid){    //  This TCP_Client wants to use a UNIX domain socket instead of the    //  inet socket for communication.  Create the new socket owned by the    //  requested user and pass the name back to the client.    //  Unset TMPDIR to ensure that tempnam() works as desired    unsetenv("TMPDIR");    char *tmpfile = tempnam("/tmp", ".fam");#if defined(__FreeBSD__)    sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };#else    sockaddr_un sun = { AF_UNIX, "" };#endif    if(strlen(tmpfile) >= (sizeof(sun.sun_path) - 1))    {        Log::error("tmpnam() too long for sun_path (%d >= %d)!",                   strlen(tmpfile), sizeof(sun.sun_path) - 1);        free(tmpfile);        return;    }    strcpy(sun.sun_path, tmpfile);    free(tmpfile);    Cred::SuperUser.become_user();    int client_sock = socket(PF_UNIX, SOCK_STREAM, 0);    if (client_sock < 0)    {   Log::perror("localclient socket(PF_UNIX, SOCK_STREAM, 0)");	return;    }    Log::debug("client %s said uid %d; creating %s",               inet_client.name(), uid, sun.sun_path);    unlink(sun.sun_path);    if (bind(client_sock, (sockaddr *) &sun, sizeof sun) != 0)    {   Log::perror("localclient bind");	close(client_sock);        return;    }    if (chmod(sun.sun_path, 0600) != 0)    {   Log::perror("localclient chmod");	close(client_sock);        return;    }    if (chown(sun.sun_path, uid, (gid_t)-1) != 0)    {   Log::perror("localclient chown");	close(client_sock);        return;    }    //  Since we're going to start listening on this socket, set a task    //  to clean it up if we don't receive a connection within 60 seconds.    NegotiatingClient *nc = new NegotiatingClient(client_sock, uid, &sun);    negotiating_clients.insert(client_sock, nc);    timeval nto;    gettimeofday(&nto, NULL);    nto.tv_sec += 60;  //  XXX that should be configurable    Scheduler::install_onetime_task(nto, cleanup_negotiation, nc);    if (listen(client_sock, 1) != 0)    {   Log::perror("localclient listen");	close(client_sock);        return;    }    Scheduler::install_read_handler(client_sock, accept_localclient, NULL);    Log::debug("listening for requests for uid %d on descriptor %d (%s)",               uid, client_sock, sun.sun_path);    inet_client.send_sockaddr_un(sun);}voidListener::accept_localclient(int ofd, void *){    NegotiatingClient *nc = negotiating_clients.find(ofd);    assert(nc);    // Get the new socket.#if defined(__FreeBSD__)    struct sockaddr_un sun = { sizeof(sockaddr_un), AF_UNIX, "" };#else    struct sockaddr_un sun = { AF_UNIX, "" };#endif    CONFIG_SOCKLEN_T sunlen = sizeof(sun);    int client_fd = accept(ofd, (struct sockaddr *) &sun, &sunlen);    if (client_fd < 0)    {	Log::perror("failed to accept new client");	return;    }    //  Keep the scheduler from helpfully cleaning this up.    Scheduler::remove_onetime_task(cleanup_negotiation, nc);    Log::debug("client fd %d is local/trusted (socket %s, uid %d).",               client_fd, nc->sun.sun_path, nc->uid);    Cred cred(nc->uid, client_fd);    new LocalClient(client_fd, &(nc->sun), cred);    // We don't need a reference to this object.  The constructor    // takes care of registering it with the Scheduler.    //  Stop listening for new connections on that socket.    Scheduler::remove_read_handler(ofd);    close(ofd);    //  This client is no longer negotiating, so remove it from the list    //  and delete it.    negotiating_clients.remove(ofd);    delete nc;}//  Here's the problem.  If the network is stopped and restarted,//  inetd and portmapper are killed and new ones are launched in their//  place.  Since they have no idea that fam is already running, inetd//  opens a new TCP/IP socket and registers it with the new portmapper.//  Meanwhile, the old fam has /dev/imon open, and any new fam launched//  by the new inetd won't be able to use imon.  The old fam can't just//  quit; it has all the state of all its existing clients to contend with.//  So here's the dirty, ugly, hack solution.  The first fam to run is//  the master.  The master opens the UNIX domain socket,//  /tmp/.fam_socket, and listens for connections from slaves.  Each//  subsequent fam is a slave.  It knows it's a slave because it can//  connect to the master.  The slave passes its rendezvous descriptor//  (the one that fam clients will connect to) through to the master//  using the access rights mechanism of Unix domain sockets.  The//  master receives the new descriptor and starts accepting clients on//  it.  Meanwhile, the slave blocks, waiting for the master to close//  the connection on /tmp/.fam_socket.  That happens when the master//  exits.//  This master/slave stuff has nothing to do with fam forwarding//  requests to fams on other hosts.  That's called client/server//  fams.//  Why not just kill fam when we kill the rest of the network//  services?  Because too many programs need it.  The desktop, the//  desktop sysadmin tools, MediaMail...  None of those should go down//  when the network goes down, and none of them are designed to//  handle fam dying.voidListener::dirty_ugly_hack(){#if defined(__FreeBSD__)    static sockaddr_un sun = { sizeof (sockaddr_un), AF_UNIX, "/tmp/.fam_socket" };#else    static sockaddr_un sun = { AF_UNIX, "/tmp/.fam_socket" };#endif    int sock = socket(PF_UNIX, SOCK_STREAM, 0);    if (sock < 0)    {   Log::perror("socket(PF_UNIX, SOCK_STREAM, 0)");	exit(1);    }        struct stat sb;    if (lstat(sun.sun_path, &sb) == 0 &&	sb.st_uid == 0 && S_ISSOCK(sb.st_mode))    {	if (connect(sock, (sockaddr *) &sun, sizeof sun) == 0)	{   	    // Another fam is listening to /tmp/.fam_socket.	    // Pass our rendezvous fd to the other fam and	    // sleep until that fam exits.	    int rights[1] = { rendezvous_fd };	    msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };	    if (sendmsg(sock, &msg, 0) < 0)	    {   Log::perror("sendmsg");		exit(1);	    }	    Log::debug("fam (process %d) enslaved to master fam", getpid());	    char data[1];	    int nb = read(sock, &data, sizeof data);	    assert(nb == 0);	    exit(0);	}	else	    Log::debug("could not enslave myself: %m");    }    //  We were unable to connect to another fam.    //	We'll create our own dirty ugly hack socket and accept connections.    (void) unlink(sun.sun_path);    if (bind(sock, (sockaddr *) &sun, sizeof sun) != 0)    {   Log::perror("bind");	exit(1);    }    if (chmod(sun.sun_path, 0700) != 0)    {   Log::perror("chmod");	exit(1);    }    if (listen(sock, 1) != 0)    {   Log::perror("listen");	exit(1);    }    (void) Scheduler::install_read_handler(sock, accept_ugly_hack, this);    _ugly_sock = sock;    Log::debug("fam (process %d) is master fam.", getpid());}voidListener::accept_ugly_hack(int ugly, void *closure){    Listener *listener = (Listener *) closure;    assert(ugly == listener->_ugly_sock);        //  Accept a new ugly connection.    struct sockaddr_un sun;    CONFIG_SOCKLEN_T sunlen = sizeof sun;    int sock = accept(ugly, (struct sockaddr *)(&sun), &sunlen);    if (sock < 0)    {   Log::perror("accept");	return;    }    (void) Scheduler::install_read_handler(sock, read_ugly_hack, listener);}voidListener::read_ugly_hack(int sock, void *closure){    Listener *listener = (Listener *) closure;    int rights[1] = { -1 };    msghdr msg = { NULL, 0, NULL, 0, (caddr_t) rights, sizeof rights };    if (recvmsg(sock, &msg, 0) >= 0)    {	Log::debug("master fam (process %d) has new slave", getpid());	assert(rights[0] >= 0);	listener->set_rendezvous_fd(rights[0]);	//  Turn the inactivity timeout all the way down.  We want to	//  clean up this dirty ugly hack as soon as possible after        //  the last Activity is destroyed.	Activity::timeout(0);	// Forget socket -- it will be closed on exit.	// Slave is blocked waiting for socket to close, so slave	// will exit when master exits.	(void) Scheduler::remove_read_handler(sock);    }    else    {	Log::perror("recvmsg");	(void) Scheduler::remove_read_handler(sock);	(void) close(sock);    }}NegotiatingClient::NegotiatingClient(int fd, uid_t u, struct sockaddr_un *sunp)    : sock(fd), uid(u){    sun.sun_family = AF_UNIX;    strcpy(sun.sun_path, sunp->sun_path);}static voidcleanup_negotiation(void *closure){    NegotiatingClient *nc = (NegotiatingClient *)closure;    Log::debug("cleaning up incomplete negotiation for client %d", nc->sock);    //  Remove client from list    negotiating_clients.remove(nc->sock);    //  Remove the read handler & close the socket    Scheduler::remove_read_handler(nc->sock);    close(nc->sock);    nc->sock = -1;    //  Remove the temp file    uid_t preveuid = geteuid();    if (preveuid) seteuid(0);    seteuid(nc->uid);    unlink(nc->sun.sun_path);    if (nc->uid) seteuid(0);    seteuid(preveuid);    delete nc;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -