Back to index

im-sdk  12.3.91
IMUtil.cpp
Go to the documentation of this file.
00001 #include <config.h>
00002 #include <errno.h>
00003 #include <sys/types.h>
00004 #include <sys/stat.h>
00005 
00006 #ifndef WIN32
00007 #include <unistd.h>
00008 #include <dirent.h>
00009 #include <fcntl.h>
00010 #define DIRECTORY_SEP "/"
00011 #define IS_DIRECTORY_SEP(c) ((c) == '/')
00012 #else
00013 #define stat _stat
00014 #define chdir _chdir
00015 #define S_ISDIR(m) ((m) & _S_IFDIR)
00016 #define S_IRUSR _S_IREAD
00017 #define S_IWUSR _S_IWRITE
00018 #define DIRECTORY_SEP "\\"
00019 #define IS_DIRECTORY_SEP(c) (((c) == '/') || ((c) == '\\'))
00020 #endif
00021 
00022 #ifdef HAVE_UNIX_SOCKET
00023 #ifdef HAVE_STDDEF_H
00024 #include <stddef.h>
00025 #endif
00026 #include <sys/un.h>
00027 #endif
00028 
00029 #ifndef WIN32
00030 #include <unistd.h>
00031 #include <dirent.h>
00032 #include <netdb.h>
00033 #include <netinet/in.h>
00034 #include <sys/socket.h>
00035 #include <sys/resource.h>
00036 #else
00037 // TODO
00038 #endif
00039 
00040 #if defined(HAVE_POLL)
00041 #include <sys/poll.h>
00042 #elif defined(HAVE_SELECT)
00043 #include <sys/select.h>
00044 #endif
00045 
00046 
00047 #include "IMUtil.hh"
00048 #include "IMLog.hh"
00049 
00050 #ifdef HAVE_TLS
00051 #include <openssl/ssl.h>
00052 #include "IMTLS.hh"
00053 #endif
00054 
00055 /*******************************************************************************
00056                              DirectoryInfo
00057 *******************************************************************************/
00058 
00059 bool
00060 DirectoryInfo::addfile(
00061     const char *filename
00062 )
00063 {
00064     struct stat s;
00065     DirectoryEntry d;
00066 
00067     string full = filename;
00068     if (full.empty()) return false;
00069 
00070     char head = full.at(0);
00071     if (!IS_DIRECTORY_SEP(head)) {
00072        full = path;
00073        char tail = full.at(full.size() - 1);
00074        if (!IS_DIRECTORY_SEP(tail)) {
00075            full = full.append(DIRECTORY_SEP);
00076        }
00077        full = full.append(filename);
00078     }
00079 
00080     if (stat(full.c_str(), &s) != 0) return false;
00081     if (!subdirp && S_ISDIR(s.st_mode)) return false;
00082     if (!unreadablep && (!(s.st_mode & S_IRUSR))) return false;
00083 
00084     d.fullname = full;
00085 
00086     {
00087        string::size_type fpos;
00088 #ifdef WIN32
00089        fpos = d.fullname.find_last_of("\\/");
00090 #else
00091        fpos = d.fullname.rfind('/');
00092 #endif
00093        if (fpos == string::npos) {
00094            d.filename = d.fullname;
00095            d.dirname = "";
00096        } else {
00097            d.filename = d.fullname.substr(fpos + 1);
00098            d.dirname = d.fullname.substr(0, fpos + 1);
00099        }
00100     }
00101 
00102     d.readable_p = (s.st_mode & S_IRUSR);
00103     d.writable_p = (s.st_mode & S_IWUSR);
00104     d.directory_p = S_ISDIR(s.st_mode); 
00105     dirvec.push_back(d);
00106 
00107     return true;
00108 }
00109 
00110 bool
00111 DirectoryInfo::refresh(
00112     const char *x_path
00113 )
00114 {
00115     if (x_path) path = x_path;
00116     if (path.empty()) return false;
00117 
00118     if (!dirvec.empty()) dirvec.erase(dirvec.begin(), dirvec.end());
00119 
00120 #if defined(WIN32)
00121 #error "TODO"
00122 #elif defined(HAVE_READDIR_R)
00123     DIR *dir;
00124     struct dirent *entry;
00125     struct dirent *result;
00126     int ret;
00127 
00128     dir = opendir(path.c_str());
00129     if (!dir) return false;
00130     entry = (struct dirent*) malloc(sizeof(struct dirent) + NAME_MAX);
00131     if (!entry) return false;
00132     while (((ret = readdir_r(dir, entry, &result)) == 0)
00133           && result) {
00134        addfile(result->d_name);
00135     }
00136     free(entry);
00137     closedir(dir);
00138 #else /* normal readdir */
00139     DIR *dir;
00140     struct dirent *result;
00141 
00142     dir = opendir(path.c_str());
00143     if (!dir) return false;
00144     while (result = readdir(dir)) {
00145        addfile(result->d_name);
00146     }
00147     closedir(dir);
00148 #endif
00149 
00150     return true;
00151 }
00152 
00153 DirectoryInfo::DirectoryInfo(
00154     bool include_subdir_p,
00155     bool include_unreable_p
00156 )
00157 {
00158     subdirp = include_subdir_p;
00159     unreadablep = include_unreable_p;
00160 }
00161 
00162 /*******************************************************************************
00163                              LEDirectoryInfo
00164 *******************************************************************************/
00165 
00166 bool
00167 LEDirectoryInfo::addfile(
00168     const char *filename
00169 )
00170 {
00171     struct stat s;
00172     DirectoryEntry d;
00173 
00174     string full = filename;
00175     if (full.empty()) return false;
00176 
00177     if (stat(full.c_str(), &s) != 0) return false;
00178 
00179     d.fullname = full;
00180 
00181     {
00182        string::size_type fpos;
00183 #ifdef WIN32
00184        fpos = d.fullname.find_last_of("\\/");
00185 #else
00186        fpos = d.fullname.rfind('/');
00187 #endif
00188        if (fpos == string::npos) {
00189            d.filename = d.fullname;
00190            d.dirname = "";
00191        } else {
00192            d.filename = d.fullname.substr(fpos + 1);
00193            d.dirname = d.fullname.substr(0, fpos + 1);
00194        }
00195     }
00196 
00197     d.readable_p = (s.st_mode & S_IRUSR);
00198     d.writable_p = (s.st_mode & S_IWUSR);
00199     d.directory_p = S_ISDIR(s.st_mode); 
00200     dirvec.push_back(d);
00201 
00202     return true;
00203 }
00204 
00205 bool
00206 LEDirectoryInfo::refresh(
00207     const char *x_path
00208 )
00209 {
00210     DirectoryInfo di;
00211     DirectoryInfoVec *pdv;
00212 
00213     if (x_path) path = x_path;
00214     if (path.empty()) return false;
00215 
00216     if (!dirvec.empty()) dirvec.erase(dirvec.begin(), dirvec.end());
00217 
00218     DirectoryInfo::refresh(path.c_str());
00219 
00220     pdv = DirectoryInfo::get_directory_info();
00221     DirectoryInfoVec::const_iterator i = pdv->begin();
00222     for (; i != pdv->end();++i) {
00223        string full = i->dirname;
00224 
00225        if (0 == strcmp("locale", i->filename.c_str())) continue;
00226 
00227        char tail = full.at(full.size() - 1);
00228        if (!IS_DIRECTORY_SEP(tail)) {
00229            full = full.append(DIRECTORY_SEP);
00230        }
00231 
00232 #ifdef WIN32
00233 #define MODEXT ".dll"
00234 #else
00235 #define MODEXT ".so"
00236 #endif
00237        full = full.append(i->filename);
00238        if (i->filename.rfind(MODEXT, i->filename.size()) >= i->filename.size()) {
00239            full = full.append(DIRECTORY_SEP);
00240            full = full.append(i->filename);
00241            full = full.append(MODEXT);
00242        }
00243        addfile(full.c_str());
00244     }
00245 
00246     return true;
00247 }
00248 
00249 /*******************************************************************************
00250                              File manipulation.
00251 *******************************************************************************/
00252 
00253 CARD8BIT*
00254 IM_read_file(
00255     const char* filename,
00256     size_t size
00257 )
00258 {
00259     size_t rsize;
00260     FILE *fd;
00261     CARD8BIT *pr;
00262 
00263     fd = fopen(filename, "rb");
00264     if (!fd) {
00265        return NULL;
00266     }
00267     pr = (CARD8BIT*) malloc(sizeof(CARD8BIT) * size);
00268     if (!pr) {
00269        fclose(fd);
00270        return NULL;
00271     }
00272     rsize = fread(pr, sizeof(CARD8BIT), size, fd);
00273     fclose(fd);
00274     if (rsize != size) {
00275        free(pr);
00276        return NULL;
00277     }
00278 
00279     return pr;
00280 }
00281 
00282 // return -1 if error occurs.
00283 int IM_file_size(
00284     const char* filename
00285 )
00286 {
00287     struct stat s;
00288     if (stat(filename, &s) != 0) return -1;
00289     return s.st_size;
00290 }
00291 
00292 static int
00293 dir_valid(const char * path_name, uid_t euid) {
00294     struct stat      st;
00295 
00296 #if !defined(S_IAMB)
00297 #define       S_IAMB 0x1FF
00298 #endif /* !S_IAMB */
00299     if ((0 == lstat(path_name, &st)) &&
00300        (0 != S_ISDIR(st.st_mode)) &&
00301         (euid == st.st_uid) &&
00302         ((S_IAMB & st.st_mode) == S_IRWXU)) {
00303        return 1;
00304     } else {
00305        return 0;
00306     }
00307 }
00308 
00309 
00310 void
00311 alternate_unix_domain_socket_file_dir(
00312     string &  user_name,
00313     uid_t     euid,
00314     string &  alternate_dir
00315 )
00316 {
00317     DIR *            dirp;
00318     struct dirent *  dirent;
00319     string           base_name;
00320     string           dir_name;
00321     char *           dir_str;
00322     int                     length;
00323     int                     found;
00324     int                     fd;
00325     int                     i;
00326     int                     key;
00327 
00328 #define ALTERNATE_DIR_RETRY_MAX    (10)
00329 
00330     dirp = opendir("/tmp");
00331     if (NULL == dirp) return;
00332 
00333     base_name = (string(".iiimp-unix-") + user_name + string("-"));
00334 
00335     found = 0;
00336     while (NULL != (dirent = readdir(dirp))) {
00337        if (0 != strncmp(dirent->d_name, base_name.c_str(), base_name.size())) {
00338            continue;
00339        }
00340 
00341        dir_name = (string("/tmp/") + string(dirent->d_name));
00342        found = dir_valid(dir_name.c_str(), euid);
00343 
00344        if (1 == found) break;
00345     }
00346 
00347     closedir(dirp);
00348 
00349     if (1 == found) {
00350        alternate_dir = dir_name;
00351        return;
00352     }
00353 
00354     length = strlen("/tmp/.iiimp-unix-") + user_name.size() + 1 + 8 + 1;
00355     dir_str = (char *)malloc(length);
00356     if (NULL == dir_str) return;
00357 
00358     fd = open("/dev/urandom", O_RDONLY, 0);
00359     if (fd <= 0) srandom(time(NULL));
00360 
00361     for (i = 0; i < ALTERNATE_DIR_RETRY_MAX; i++) {
00362        if (0 <= fd) {
00363            read(fd, &key, sizeof (key));
00364        } else {
00365            key = random();
00366        }
00367 
00368        snprintf(dir_str, length,
00369                "/tmp/.iiimp-unix-%s-%08x", user_name.c_str(), key);
00370 
00371        if (mkdir(dir_str, S_IRWXU) < 0) {
00372            if (EEXIST != errno) {
00373               continue;
00374            }
00375        }
00376        if (1 == dir_valid(dir_str, euid)) {
00377            break;
00378        }
00379     }
00380 
00381     if (0 <= fd) close(fd);
00382 
00383     if (i < ALTERNATE_DIR_RETRY_MAX) {
00384        alternate_dir = string(dir_str);
00385     }
00386     free(dir_str);
00387 
00388     return;
00389 }
00390 
00391 void
00392 IM_unix_domain_socket_file_dir(string user_name, string & dir_ret)
00393 {
00394     string    dir;
00395     uid_t     euid;
00396     struct stat      st;
00397 
00398     euid = geteuid();
00399 
00400     dir = (string("/tmp/.iiimp-unix-") + string(user_name));
00401     if (0 == dir_valid(dir.c_str(), euid)) {
00402        alternate_unix_domain_socket_file_dir(user_name, euid, dir_ret);
00403     } else {
00404        dir_ret = dir;
00405     }
00406 }
00407 
00408 /*******************************************************************************
00409                             IMSocket & IMSocketListen
00410 *******************************************************************************/
00411 
00412 //
00413 // Address object.
00414 //
00415 
00416 void*
00417 IMSocketAddress::get_addrinfo()
00418 {
00419     struct addrinfo hints, *pst;
00420     int st;
00421 
00422     if (addrinfo) return addrinfo;
00423 
00424     memset(&hints, 0, sizeof(hints));
00425     hints.ai_family = PF_UNSPEC;
00426     hints.ai_socktype = SOCK_STREAM;
00427     if (!address.c_str()) hints.ai_flags = AI_PASSIVE;
00428     st = getaddrinfo(address.c_str(), service.c_str(), &hints, &pst);
00429     if (st != 0) {
00430        LOG_ERROR("Fail to getaddrinfo:%s, %s",
00431                 address.c_str(), service.c_str());
00432        return NULL;
00433     }
00434 
00435     addrinfo = pst;
00436 
00437     return pst;
00438 }
00439 
00440 void
00441 IMSocketAddress::bind_fail(
00442     int fd
00443 )
00444 {
00445     int e = errno;
00446 
00447     LOG_ERROR("Fail to bind socket: %s:%s", address.c_str(), service.c_str());
00448 #ifdef EADDRINUSE
00449     const int err_addr_in_use = EADDRINUSE;
00450 #else
00451     const int err_addr_in_use = EINVAL;
00452 #endif
00453     if (e == err_addr_in_use) {
00454        LOG_ERROR("Another IIIMP server might be running(address already in use).");
00455     }
00456 
00457     close(fd);
00458 }
00459 
00460 void
00461 IMSocketAddress::configure_socket(
00462     int fd
00463 )
00464 {
00465     int reuse = 1;
00466     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
00467 }
00468 
00469 int
00470 IMSocketAddress::setup_inet(
00471     void *paddressinfo
00472 )
00473 {
00474     struct addrinfo *pai = (struct addrinfo*) paddressinfo;
00475 
00476     int fd = socket(pai->ai_family, pai->ai_socktype, pai->ai_protocol);
00477 
00478     if (fd < 0) {
00479        LOG_ERROR("Cannot open socket for %s:%s",
00480                 address.c_str(), service.c_str());
00481        return -1;
00482     }
00483 
00484     configure_socket(fd);
00485 
00486     if (bind(fd, pai->ai_addr, pai->ai_addrlen) < 0) {
00487        bind_fail(fd);
00488        return -1;
00489     }
00490 
00491     return fd;
00492 }
00493 
00494 int
00495 IMSocketAddress::setup_unix()
00496 {
00497 
00498 #if defined(HAVE_UNIX_SOCKET)
00499     struct stat st;
00500     string srvc;
00501 
00502     srvc = service.c_str();
00503     if (srvc.size() != 0) {
00504 
00505        memset(&st, 0, sizeof(st));
00506        if (lstat(address.c_str(), &st) == 0) {
00507            if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IXUSR | S_IWUSR))
00508               && !S_ISLNK(st.st_mode)) {
00509            } else {
00510               return -1;
00511            }
00512        } else {
00513            mode_t mode = (type == UNIX_DOMAIN) ? 0755 : 0700;
00514            string dir = address.substr(0, address.find(".iiimp-unix", 0));
00515 
00516            if (lstat(dir.c_str(), &st) == 0) {
00517               if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IXUSR | S_IWUSR))
00518                   && !S_ISLNK(st.st_mode)) {
00519                   /* just try to make address directory */
00520                   if (mkdir(address.c_str(), mode) < 0) {
00521                      return -1;
00522                   }
00523               } else {
00524                   return -1;
00525               }
00526            } else {
00527               /* base directory like /var/run/iiim is missing. */
00528               if (!mkdir(dir.c_str(), mode)) {
00529                   if (mkdir(address.c_str(), mode) < 0) {
00530                      return -1;
00531                   }
00532               } else {
00533                   return -1;
00534               }
00535            }
00536        }
00537     }
00538 
00539     int fd = socket(AF_UNIX, SOCK_STREAM, 0);
00540 
00541     if (fd == -1) {
00542        LOG_ERROR("Cannot open socket. (%s)", address.c_str());
00543        return false;
00544     }
00545 
00546     configure_socket(fd);
00547 
00548     struct sockaddr_un sun_addr;
00549 
00550     if (srvc.size() != 0) {
00551        snprintf(sun_addr.sun_path, sizeof(sun_addr.sun_path), "%s/%s",
00552                address.c_str(), service.c_str());
00553     } else {
00554        strncpy(sun_addr.sun_path, address.c_str(), sizeof(sun_addr.sun_path));
00555     }
00556     sun_addr.sun_path[sizeof(sun_addr.sun_path) -1] = '\0';
00557     sun_addr.sun_family = AF_UNIX;
00558 
00559     size_t size = (offsetof(struct sockaddr_un, sun_path)
00560        + strlen (sun_addr.sun_path) + 1);
00561 
00562     if (unlink(sun_addr.sun_path) < 0) {
00563        if (errno != ENOENT) {
00564               bind_fail (fd);
00565               return -1;
00566        }
00567     }
00568 
00569     mode_t new_mask = (type == UNIX_DOMAIN) ? 0 : 077;
00570     mode_t old_mask = umask(new_mask);
00571     if (bind(fd, (struct sockaddr*) &sun_addr, size) < 0) {
00572        bind_fail(fd);
00573        unlink(sun_addr.sun_path);
00574        return -1;
00575     }
00576     umask(old_mask);
00577 
00578     return fd;
00579 #else
00580     return -1;
00581 #endif
00582 }
00583 
00584 int
00585 IMSocketAddress::create_sockets(
00586     IMSocketVec &sockvec
00587 )
00588 {
00589     if (type == INET) {
00590        int i = 0;
00591        int fd;
00592        struct addrinfo *pai = (struct addrinfo*) get_addrinfo();
00593        IMSocket *ps;
00594 
00595        for (; pai; pai = pai->ai_next) {
00596            fd = setup_inet(pai);
00597            if (fd < 0) continue;
00598            ps = new IMSocket(fd, trans_type);
00599            // it should be dealt with memory exception.
00600            if (!ps) {
00601               close(fd);
00602               return -1;
00603            }
00604            sockvec.push_back(ps);
00605            i++;
00606        }
00607 
00608        if (i == 0) {
00609            LOG_CRITICAL("Failed to create any sockets for %s:%s ... exit.",
00610                       address.c_str(), service.c_str());
00611            exit(255);
00612        }
00613 
00614        return i;
00615     } else if (type == UNIX_DOMAIN || type == UNIX_DOMAIN_PER_USER) {
00616        IMSocket *ps;
00617        int fd = setup_unix();
00618 
00619        if (fd >= 0) {
00620            ps = new IMSocket(fd, trans_type);
00621            if (!ps) {
00622               close(fd);
00623               return -1;
00624            }
00625        }
00626        if (fd < 0) {
00627            LOG_CRITICAL("Failed to create a unix domain socket for %s:%s ... exit.",
00628                       address.c_str(), service.c_str());
00629            exit(255);
00630        }
00631 
00632        sockvec.push_back(ps);
00633        return 1;
00634     }
00635 
00636     ERROR_INTERNAL("invalid IMSocketAddress type");
00637 
00638     return 0;
00639 }
00640 
00641 IMSocketAddress::~IMSocketAddress()
00642 {
00643     if (addrinfo) {
00644        freeaddrinfo((struct addrinfo*) addrinfo);
00645     }
00646 #if defined(HAVE_UNIX_SOCKET)
00647     if (type == UNIX_DOMAIN || type == UNIX_DOMAIN_PER_USER) {
00648         sockaddr_un sun_addr;
00649        string dir = address.substr(0, address.find(".iiimp-unix", 0));
00650 
00651         snprintf(sun_addr.sun_path, sizeof(sun_addr.sun_path), "%s/%s",
00652             address.c_str(), service.c_str());
00653         sun_addr.sun_path[sizeof(sun_addr.sun_path) -1] = '\0';
00654         unlink (sun_addr.sun_path);
00655        if (is_dir_created || dir != address) {
00656            /* try to remove a socket directory which was made by iiimd itself
00657               or was expected for IIIMF */
00658            rmdir(address.c_str());
00659        }
00660     }
00661 #endif
00662 }
00663 
00664 IMSocketAddress::
00665 IMSocketAddress(
00666     ADDRESS_TYPE x_type,
00667     const string &x_address,
00668     const string &x_service
00669 ) : trans_type(NORMAL), type(x_type), address(x_address), service(x_service)
00670 {
00671     addrinfo = NULL;
00672     is_dir_created = false;
00673 }
00674 
00675 IMSocketAddress::
00676 IMSocketAddress(
00677     ADDRESS_TYPE x_type,
00678     TRANSPORT_TYPE x_trans,
00679     const string &x_address,
00680     const string &x_service
00681 ) : trans_type(x_trans), type(x_type), address(x_address), service(x_service)
00682 {
00683     addrinfo = NULL;
00684     is_dir_created = false;
00685 }
00686 
00687 //
00688 // Socket object.
00689 //
00690 
00691 bool
00692 IMSocket::do_listen()
00693 {
00694     if (::listen(fd, 5) < 0) {
00695        LOG_ERROR("Fail to listen:%d", fd);
00696        close(fd);
00697        status = ERROR;
00698        return false;
00699     }
00700 
00701     status = OPEN;
00702 
00703     return true;
00704 }
00705 
00706 bool
00707 IMSocket::listen()
00708 {
00709     if (status == ERROR) return false;
00710     if (status == OPEN) return true;
00711 
00712     return do_listen();
00713 }
00714 
00715 IMSocketTrans*
00716 IMSocket::accept()
00717 {
00718     int st;
00719 #ifdef HAVE_SOCKADDR_STORAGE
00720     struct sockaddr_storage ss;
00721 #else
00722     struct sockaddr ss;
00723 #endif
00724     struct sockaddr *psa = (struct sockaddr*) &ss;
00725     socklen_t fromlen;
00726 
00727     if (status == ERROR) return NULL;
00728 
00729     do {
00730        fromlen = sizeof(ss);
00731        st = ::accept(fd, psa, &fromlen);
00732        if (st != -1) {
00733 #ifdef HAVE_TLS
00734             return IMTLS::get_instance()->create_trans(st, trans_type);
00735 #else
00736            return new IMSocketTrans(st);
00737 #endif
00738        }
00739     } while (errno == EINTR);
00740 
00741     status = ERROR;
00742     LOG_ERROR("Could not accept.\n");
00743 
00744     return NULL;
00745 }
00746 
00747 int
00748 IMSocket::get_fd()
00749 {
00750     if (status == OPEN) return fd;
00751     return -1;
00752 }
00753 
00754 IMSocket::IMSocket(
00755     int x_fd
00756 ) : fd(x_fd), status(CLOSED), trans_type(IMSocketAddress::NORMAL)
00757 {
00758 }
00759 
00760 IMSocket::~IMSocket()
00761 {
00762     if (status == OPEN) {
00763        close(fd);
00764     }
00765 }
00766 
00767 IMSocket::IMSocket(
00768     int x_fd,
00769     int x_trans
00770 ) : fd(x_fd), status(CLOSED), trans_type(x_trans)
00771 {
00772 }
00773 
00774 //
00775 // Trans object.
00776 //
00777 
00778 int
00779 IMSocketTrans::send(
00780     const void *p,
00781     size_t n
00782 )
00783 {
00784     return ::send(fd, p, n, 0);
00785 }
00786 
00787 int
00788 IMSocketTrans::recv(
00789     void *p,
00790     size_t n
00791 )
00792 {
00793     return ::recv(fd, p, n, 0);
00794 }
00795 
00796 IMSocketTrans::IMSocketTrans(
00797     int x_fd
00798 )
00799 {
00800     fd = x_fd;
00801 }
00802 
00803 IMSocketTrans::~IMSocketTrans()
00804 {
00805     if (fd > 0) {
00806        shutdown(fd, SHUT_RDWR);
00807        close(fd);
00808     }
00809 }
00810 
00811 
00812 //
00813 // Listen object.
00814 //
00815 
00816 bool
00817 IMSocketListen::listen(
00818     IMSocketAddressVec& endvec
00819 )
00820 {
00821     if (status == IMSocket::ERROR) return false;
00822     if (status == IMSocket::OPEN) return true;
00823 
00824     IMSocketAddressVec::iterator ait;
00825     for (ait = endvec.begin(); ait!= endvec.end(); ait++) {
00826        if (ait->create_sockets(sockvec) < 0) return false;
00827     }
00828 
00829     IMSocketVec::iterator it;
00830     IMSocket *pims;
00831     for (it = sockvec.begin(); it != sockvec.end(); it++) {
00832        pims = *it;
00833        if (pims->get_status() == IMSocket::ERROR) return false;
00834        if (pims->get_status() == IMSocket::OPEN) continue;
00835        if (!pims->listen()) {
00836            status = IMSocket::ERROR;
00837            return false;
00838        }
00839     }
00840     status = IMSocket::OPEN;
00841 
00842     return true;
00843 }
00844 
00845 IMSocketTrans*
00846 IMSocketListen::accept()
00847 {
00848     IMSocketVec::iterator it;
00849     IMSocket *pims;
00850 
00851     if (status != IMSocket::OPEN) return NULL;
00852 
00853 #if defined(HAVE_POLL)
00854     int fd, i, n;
00855     struct pollfd *fds;
00856 
00857     i = sockvec.size();
00858     if (i <= 0) return NULL;
00859     fds = new struct pollfd[i];
00860     if (!fds) return NULL;
00861     for (n = 0, it = sockvec.begin(); it != sockvec.end(); it++) {
00862        pims = *it;
00863        fd = pims->get_fd();
00864        if (fd < 0) continue;
00865        fds[n].fd = fd;
00866        fds[n].events = POLLIN;
00867        fds[n].revents = 0;
00868        n++;
00869     }
00870     if (n > 0) {
00871        i = poll(fds, n, -1);
00872        if (i > 0) {
00873            for (i = 0; i < n; i++) {
00874               if (fds[i].revents) {
00875                   fd = fds[i].fd;
00876                   for (it = sockvec.begin(); it != sockvec.end(); it++) {
00877                      pims = *it;
00878                      if (fd == pims->get_fd()) {
00879                          delete [] fds;
00880                          return pims->accept();
00881                      }
00882                   }
00883               }
00884            }
00885        }
00886     }
00887     delete [] fds;
00888 
00889 #elif defined(HAVE_SELECT)
00890     int fd, maxfd;
00891     fd_set fds;
00892     FD_ZERO(&fds);
00893 
00894     maxfd = -1;
00895     for (it = sockvec.begin(); it != sockvec.end(); it++) {
00896        pims = *it;
00897        fd = pims->get_fd();
00898        if (fd < 0) continue;
00899        if (fd > maxfd) maxfd = fd;
00900        FD_SET(fd, &fds);
00901     }
00902     if (maxfd < 0) return NULL;
00903     maxfd++;
00904     fd = select(maxfd, &fds, NULL, NULL, NULL);
00905     if (fd <= 0) return NULL;
00906     for (it = sockvec.begin(); it != sockvec.end(); it++) {
00907        pims = *it;
00908        fd = pims->get_fd();
00909        if (fd < 0) continue;
00910        if (FD_ISSET(fd, &fds)) {
00911            fd = pims->accept();
00912            if (fd < 0) return NULL;
00913            return fd;
00914        }
00915     }
00916 #else
00917 #error "We need poll() or select() to multiplex socket handles"
00918 #endif
00919 
00920     return NULL;
00921 }
00922 
00923 IMSocketListen::IMSocketListen()
00924 {
00925     status = IMSocket::CLOSED;
00926 }
00927 
00928 IMSocketListen::~IMSocketListen()
00929 {
00930     delete_all(sockvec);
00931 }
00932 
00933 /*******************************************************************************
00934                     IMDaemon (helper object to make a program a daemon)
00935 *******************************************************************************/
00936 
00937 IMDaemon* IMDaemon::pimdaemon = NULL;
00938 
00939 IMDaemon::IMDaemon()
00940 {
00941     already_finished = false;
00942 }
00943 
00944 IMDaemon*
00945 IMDaemon::get_instance()
00946 {
00947     if (pimdaemon) return pimdaemon;
00948     pimdaemon = new IMDaemon();
00949     return pimdaemon;
00950 }
00951 
00952 void
00953 IMDaemon::cleanup()
00954 {
00955     if (pimdaemon) delete pimdaemon;
00956 }
00957 
00958 void
00959 IMDaemon::closefd()
00960 {
00961     int fd;
00962 #ifdef HAVE_SYSCONF
00963     int fdmax = sysconf(_SC_OPEN_MAX);
00964 #else
00965     int fdmax = 3;
00966 #endif
00967     for (fd = 0; fd < fdmax; fd++) {
00968        close(fd);
00969     }
00970 
00971 #ifndef WIN32
00972     // 0 : discard stdout
00973     open("/dev/null", O_RDWR);
00974     // 1 : discard stdin
00975     dup(0);
00976     // 2 : discard stderr
00977     dup(0);
00978 #endif
00979 }
00980 
00981 bool
00982 IMDaemon::discard_parent()
00983 {
00984 #ifdef HAVE_FORK
00985     pid_t pid = fork();
00986     if (pid != 0) {
00987        if (pid == -1) {
00988            return false;
00989        } else {
00990            _exit(0);
00991        }
00992     }
00993 #endif
00994     return true;
00995 }
00996 
00997 bool
00998 IMDaemon::daemonize()
00999 {
01000     if (!discard_parent()) return false;
01001 #ifdef HAVE_SETSID
01002     if (setsid() < 0)
01003        return false;
01004 #endif
01005     if (!discard_parent()) return false;
01006 
01007     return true;
01008 }
01009 
01010 void
01011 IMDaemon::setup(
01012     const char* todir
01013 )
01014 {
01015     if (!already_finished) {
01016        if (!daemonize()) {
01017            LOG_ERROR("Fail to make a daemon.");
01018        }
01019        closefd();
01020     }
01021     if (todir) {
01022        chdir(todir);
01023     }
01024 }
01025 
01026 /* Local Variables: */
01027 /* c-file-style: "iiim-project" */
01028 /* End: */