This commit is contained in:
Jean-Francois Dockes 2016-03-22 13:50:49 +01:00
parent 90afc535a4
commit ae2962a41c
6 changed files with 785 additions and 703 deletions

File diff suppressed because it is too large Load diff

View file

@ -21,27 +21,27 @@
#include <vector> #include <vector>
#include <stack> #include <stack>
/** /**
* Callback function object to advise of new data arrival, or just periodic * Callback function object to advise of new data arrival, or just periodic
* heartbeat if cnt is 0. * heartbeat if cnt is 0.
* *
* To interrupt the command, the code using ExecCmd should either * To interrupt the command, the code using ExecCmd should either
* raise an exception inside newData() (and catch it in doexec's caller), or * raise an exception inside newData() (and catch it in doexec's caller), or
* call ExecCmd::setKill() * call ExecCmd::setKill()
* *
*/ */
class ExecCmdAdvise { class ExecCmdAdvise {
public: public:
virtual ~ExecCmdAdvise() {} virtual ~ExecCmdAdvise() {}
virtual void newData(int cnt) = 0; virtual void newData(int cnt) = 0;
}; };
/** /**
* Callback function object to get more input data. Data has to be provided * Callback function object to get more input data. Data has to be provided
* into the initial input string, set it to empty to signify eof. * into the initial input string, set it to empty to signify eof.
*/ */
class ExecCmdProvide { class ExecCmdProvide {
public: public:
virtual ~ExecCmdProvide() {} virtual ~ExecCmdProvide() {}
virtual void newData() = 0; virtual void newData() = 0;
}; };
@ -51,43 +51,43 @@ class ExecCmdProvide {
* asynchronous io as appropriate for things to work). * asynchronous io as appropriate for things to work).
* *
* Input to the command can be provided either once in a parameter to doexec * Input to the command can be provided either once in a parameter to doexec
* or provided in chunks by setting a callback which will be called to * or provided in chunks by setting a callback which will be called to
* request new data. In this case, the 'input' parameter to doexec may be * request new data. In this case, the 'input' parameter to doexec may be
* empty (but not null) * empty (but not null)
* *
* Output from the command is normally returned in a single string, but a * Output from the command is normally returned in a single string, but a
* callback can be set to be called whenever new data arrives, in which case * callback can be set to be called whenever new data arrives, in which case
* it is permissible to consume the data and erase the string. * it is permissible to consume the data and erase the string.
* *
* Note that SIGPIPE should be ignored and SIGCLD blocked when calling doexec, * Note that SIGPIPE should be ignored and SIGCLD blocked when calling doexec,
* else things might fail randomly. (This is not done inside the class because * else things might fail randomly. (This is not done inside the class because
* of concerns with multithreaded programs). * of concerns with multithreaded programs).
* *
*/ */
class ExecCmd { class ExecCmd {
public: public:
// Use vfork instead of fork. Our vfork usage is multithread-compatible as // Use vfork instead of fork. Our vfork usage is multithread-compatible as
// far as I can see, but just in case... // far as I can see, but just in case...
static void useVfork(bool on); static void useVfork(bool on);
/** /**
* Add/replace environment variable before executing command. This must * Add/replace environment variable before executing command. This must
* be called before doexec() to have an effect (possibly multiple * be called before doexec() to have an effect (possibly multiple
* times for several variables). * times for several variables).
* @param envassign an environment assignment string ("name=value") * @param envassign an environment assignment string ("name=value")
*/ */
void putenv(const std::string &envassign); void putenv(const std::string& envassign);
void putenv(const std::string &name, const std::string& value); void putenv(const std::string& name, const std::string& value);
/** /**
* Try to set a limit on child process vm size. This will use * Try to set a limit on child process vm size. This will use
* setrlimit() and RLIMIT_AS/VMEM if available. Parameter is in * setrlimit() and RLIMIT_AS/VMEM if available. Parameter is in
* units of 2**10. Must be called before starting the command, default * units of 2**10. Must be called before starting the command, default
* is inherit from parent. * is inherit from parent.
*/ */
void setrlimit_as(int mbytes); void setrlimit_as(int mbytes);
/** /**
* Set function objects to call whenever new data is available or on * Set function objects to call whenever new data is available or on
* select timeout. The data itself is stored in the output string. * select timeout. The data itself is stored in the output string.
* Must be set before calling doexec. * Must be set before calling doexec.
@ -101,59 +101,60 @@ class ExecCmd {
void setProvide(ExecCmdProvide *p); void setProvide(ExecCmdProvide *p);
/** /**
* Set select timeout in milliseconds. The default is 1 S. * Set select timeout in milliseconds. The default is 1 S.
* This is NOT a time after which an error will occur, but the period of * This is NOT a time after which an error will occur, but the period of
* the calls to the advise routine (which normally checks for cancellation). * the calls to the advise routine (which normally checks for cancellation).
*/ */
void setTimeout(int mS); void setTimeout(int mS);
/** /**
* Set destination for stderr data. The default is to let it alone (will * Set destination for stderr data. The default is to let it alone (will
* usually go to the terminal or to wherever the desktop messages go). * usually go to the terminal or to wherever the desktop messages go).
* There is currently no option to put stderr data into a program variable * There is currently no option to put stderr data into a program variable
* If the parameter can't be opened for writing, the command's * If the parameter can't be opened for writing, the command's
* stderr will be closed. * stderr will be closed.
*/ */
void setStderr(const std::string &stderrFile); void setStderr(const std::string& stderrFile);
/** /**
* Execute command. * Execute command.
* *
* Both input and output can be specified, and asynchronous * Both input and output can be specified, and asynchronous
* io (select-based) is used to prevent blocking. This will not * io (select-based) is used to prevent blocking. This will not
* work if input and output need to be synchronized (ie: Q/A), but * work if input and output need to be synchronized (ie: Q/A), but
* works ok for filtering. * works ok for filtering.
* The function is exception-safe. In case an exception occurs in the * The function is exception-safe. In case an exception occurs in the
* advise callback, fds and pids will be cleaned-up properly. * advise callback, fds and pids will be cleaned-up properly.
* *
* @param cmd the program to execute. This must be an absolute file name * @param cmd the program to execute. This must be an absolute file name
* or exist in the PATH. * or exist in the PATH.
* @param args the argument vector (NOT including argv[0]). * @param args the argument vector (NOT including argv[0]).
* @param input Input to send TO the command. * @param input Input to send TO the command.
* @param output Output FROM the command. * @param output Output FROM the command.
* @return the exec output status (0 if ok), or -1 * @return the exec output status (0 if ok), or -1
*/ */
int doexec(const std::string &cmd, const std::vector<std::string>& args, int doexec(const std::string& cmd, const std::vector<std::string>& args,
const std::string *input = 0, const std::string *input = 0,
std::string *output = 0); std::string *output = 0);
/** Same as doexec but cmd and args in one vector */ /** Same as doexec but cmd and args in one vector */
int doexec1(const std::vector<std::string>& args, int doexec1(const std::vector<std::string>& args,
const std::string *input = 0, const std::string *input = 0,
std::string *output = 0) { std::string *output = 0) {
if (args.empty()) if (args.empty()) {
return -1; return -1;
}
return doexec(args[0], return doexec(args[0],
std::vector<std::string>(args.begin() + 1, args.end()), std::vector<std::string>(args.begin() + 1, args.end()),
input, output); input, output);
} }
/* /*
* The next four methods can be used when a Q/A dialog needs to be * The next four methods can be used when a Q/A dialog needs to be
* performed with the command * performed with the command
*/ */
int startExec(const std::string &cmd, const std::vector<std::string>& args, int startExec(const std::string& cmd, const std::vector<std::string>& args,
bool has_input, bool has_output); bool has_input, bool has_output);
int send(const std::string& data); int send(const std::string& data);
int receive(std::string& data, int cnt = -1); int receive(std::string& data, int cnt = -1);
@ -162,16 +163,16 @@ class ExecCmd {
/** Read line. Timeout after timeosecs seconds */ /** Read line. Timeout after timeosecs seconds */
int getline(std::string& data, int timeosecs); int getline(std::string& data, int timeosecs);
int wait(); int wait();
/** Wait with WNOHANG set. /** Wait with WNOHANG set.
@return true if process exited, false else. @return true if process exited, false else.
@param O: status, the wait(2) call's status value */ @param O: status, the wait(2) call's status value */
bool maybereap(int *status); bool maybereap(int *status);
pid_t getChildPid(); pid_t getChildPid();
/** /**
* Cancel/kill command. This can be called from another thread or * Cancel/kill command. This can be called from another thread or
* from the advise callback, which could also raise an exception * from the advise callback, which could also raise an exception
* to accomplish the same thing. In the owner thread, any I/O loop * to accomplish the same thing. In the owner thread, any I/O loop
@ -187,7 +188,7 @@ class ExecCmd {
void zapChild(); void zapChild();
/** /**
* Request process termination (SIGTERM or equivalent). This returns * Request process termination (SIGTERM or equivalent). This returns
* immediately * immediately
*/ */
bool requestChildExit(); bool requestChildExit();
@ -197,8 +198,8 @@ class ExecCmd {
// a viewer. The default is to hide the window, // a viewer. The default is to hide the window,
// because it avoids windows appearing and // because it avoids windows appearing and
// disappearing when executing stuff for previewing // disappearing when executing stuff for previewing
EXF_SHOWWINDOW = 1, EXF_SHOWWINDOW = 1,
}; };
ExecCmd(int flags = 0); ExecCmd(int flags = 0);
~ExecCmd(); ~ExecCmd();
@ -221,37 +222,39 @@ class ExecCmd {
static bool backtick(const std::vector<std::string> cmd, std::string& out); static bool backtick(const std::vector<std::string> cmd, std::string& out);
class Internal; class Internal;
private: private:
Internal *m; Internal *m;
/* Copyconst and assignment are private and forbidden */ /* Copyconst and assignment are private and forbidden */
ExecCmd(const ExecCmd &) {} ExecCmd(const ExecCmd&) {}
ExecCmd& operator=(const ExecCmd &) {return *this;}; ExecCmd& operator=(const ExecCmd&) {
return *this;
};
}; };
/** /**
* Rexecute self process with the same arguments. * Rexecute self process with the same arguments.
* *
* Note that there are some limitations: * Note that there are some limitations:
* - argv[0] has to be valid: an executable name which will be found in * - argv[0] has to be valid: an executable name which will be found in
* the path when exec is called in the initial working directory. This is * the path when exec is called in the initial working directory. This is
* by no means guaranteed. The shells do this, but argv[0] could be an * by no means guaranteed. The shells do this, but argv[0] could be an
* arbitrary string. * arbitrary string.
* - The initial working directory must be found and remain valid. * - The initial working directory must be found and remain valid.
* - We don't try to do anything with fd 0,1,2. If they were changed by the * - We don't try to do anything with fd 0,1,2. If they were changed by the
* program, their initial meaning won't be the same as at the moment of the * program, their initial meaning won't be the same as at the moment of the
* initial invocation. * initial invocation.
* - We don't restore the signals. Signals set to be blocked * - We don't restore the signals. Signals set to be blocked
* or ignored by the program will remain ignored even if this was not their * or ignored by the program will remain ignored even if this was not their
* initial state. * initial state.
* - The environment is also not restored. * - The environment is also not restored.
* - Others system aspects ? * - Others system aspects ?
* - Other program state: application-dependant. Any external cleanup * - Other program state: application-dependant. Any external cleanup
* (temp files etc.) must be performed by the application. ReExec() * (temp files etc.) must be performed by the application. ReExec()
* duplicates the atexit() function to make this easier, but the * duplicates the atexit() function to make this easier, but the
* ReExec().atexit() calls must be done explicitely, this is not automatic * ReExec().atexit() calls must be done explicitely, this is not automatic
* *
* In short, this is usable in reasonably controlled situations and if there * In short, this is usable in reasonably controlled situations and if there
* are no security issues involved, but this does not perform miracles. * are no security issues involved, but this does not perform miracles.
*/ */
class ReExec { class ReExec {
@ -259,13 +262,14 @@ public:
ReExec() {} ReExec() {}
ReExec(int argc, char *argv[]); ReExec(int argc, char *argv[]);
void init(int argc, char *argv[]); void init(int argc, char *argv[]);
int atexit(void (*function)(void)) int atexit(void (*function)(void)) {
{ m_atexitfuncs.push(function);
m_atexitfuncs.push(function); return 0;
return 0;
} }
void reexec(); void reexec();
const std::string& getreason() {return m_reason;} const std::string& getreason() {
return m_reason;
}
// Insert new args into the initial argv. idx designates the place // Insert new args into the initial argv. idx designates the place
// before which the new args are inserted (the default of 1 // before which the new args are inserted (the default of 1
// inserts after argv[0] which would probably be an appropriate // inserts after argv[0] which would probably be an appropriate

View file

@ -84,9 +84,9 @@ using namespace std;
static const int one = 1; static const int one = 1;
static const int zero = 0; static const int zero = 0;
#define LOGSYSERR(who, call, spar) \ #define LOGSYSERR(who, call, spar) \
LOGERR(("%s: %s(%s) errno %d (%s)\n", who, call, \ LOGERR(("%s: %s(%s) errno %d (%s)\n", who, call, \
spar, errno, strerror(errno))) spar, errno, strerror(errno)))
#ifndef MIN #ifndef MIN
#define MIN(a,b) (a<b?a:b) #define MIN(a,b) (a<b?a:b)
@ -99,7 +99,7 @@ static const int zero = 0;
#endif #endif
#define MILLIS(OLD, NEW) ( (long)(((NEW).tv_sec - (OLD).tv_sec) * 1000 + \ #define MILLIS(OLD, NEW) ( (long)(((NEW).tv_sec - (OLD).tv_sec) * 1000 + \
((NEW).tv_usec - (OLD).tv_usec) / 1000)) ((NEW).tv_usec - (OLD).tv_usec) / 1000))
// Static method // Static method
// Simplified interface to 'select()'. Only use one fd, for either // Simplified interface to 'select()'. Only use one fd, for either
@ -116,12 +116,12 @@ int Netcon::select1(int fd, int timeo, int write)
FD_ZERO(&rd); FD_ZERO(&rd);
FD_SET(fd, &rd); FD_SET(fd, &rd);
if (write) { if (write) {
ret = select(fd+1, 0, &rd, 0, &tv); ret = select(fd + 1, 0, &rd, 0, &tv);
} else { } else {
ret = select(fd+1, &rd, 0, 0, &tv); ret = select(fd + 1, &rd, 0, 0, &tv);
} }
if (!FD_ISSET(fd, &rd)) { if (!FD_ISSET(fd, &rd)) {
LOGDEB2(("Netcon::select1: fd %d timeout\n",fd)); LOGDEB2(("Netcon::select1: fd %d timeout\n", fd));
} }
return ret; return ret;
} }
@ -199,11 +199,11 @@ int SelectLoop::doLoop()
// Walk the netcon map and set up the read and write fd_sets // Walk the netcon map and set up the read and write fd_sets
// for select() // for select()
nfds = 0; nfds = 0;
for (map<int,NetconP>::iterator it = m_polldata.begin(); for (map<int, NetconP>::iterator it = m_polldata.begin();
it != m_polldata.end(); it++) { it != m_polldata.end(); it++) {
NetconP &pll = it->second; NetconP& pll = it->second;
int fd = it->first; int fd = it->first;
LOGDEB2(("Selectloop: fd %d flags 0x%x\n",fd, pll->m_wantedEvents)); LOGDEB2(("Selectloop: fd %d flags 0x%x\n", fd, pll->m_wantedEvents));
if (pll->m_wantedEvents & Netcon::NETCONPOLL_READ) { if (pll->m_wantedEvents & Netcon::NETCONPOLL_READ) {
FD_SET(fd, &rd); FD_SET(fd, &rd);
nfds = MAX(nfds, fd + 1); nfds = MAX(nfds, fd + 1);
@ -275,7 +275,7 @@ int SelectLoop::doLoop()
continue; continue;
} }
map<int,NetconP>::iterator it = m_polldata.find(fd); map<int, NetconP>::iterator it = m_polldata.find(fd);
if (it == m_polldata.end()) { if (it == m_polldata.end()) {
/// This should not happen actually /// This should not happen actually
LOGDEB2(("Netcon::selectloop: fd %d not found\n", fd)); LOGDEB2(("Netcon::selectloop: fd %d not found\n", fd));
@ -284,14 +284,14 @@ int SelectLoop::doLoop()
// Next start will be one beyond last serviced (modulo nfds) // Next start will be one beyond last serviced (modulo nfds)
m_placetostart = fd + 1; m_placetostart = fd + 1;
NetconP &pll = it->second; NetconP& pll = it->second;
if (canread && pll->cando(Netcon::NETCONPOLL_READ) <= 0) { if (canread && pll->cando(Netcon::NETCONPOLL_READ) <= 0) {
pll->m_wantedEvents &= ~Netcon::NETCONPOLL_READ; pll->m_wantedEvents &= ~Netcon::NETCONPOLL_READ;
} }
if (canwrite && pll->cando(Netcon::NETCONPOLL_WRITE) <= 0) { if (canwrite && pll->cando(Netcon::NETCONPOLL_WRITE) <= 0) {
pll->m_wantedEvents &= ~Netcon::NETCONPOLL_WRITE; pll->m_wantedEvents &= ~Netcon::NETCONPOLL_WRITE;
} }
if (!(pll->m_wantedEvents & (Netcon::NETCONPOLL_WRITE|Netcon::NETCONPOLL_READ))) { if (!(pll->m_wantedEvents & (Netcon::NETCONPOLL_WRITE | Netcon::NETCONPOLL_READ))) {
LOGDEB0(("Netcon::selectloop: fd %d has 0x%x mask, erasing\n", LOGDEB0(("Netcon::selectloop: fd %d has 0x%x mask, erasing\n",
it->first, it->second->m_wantedEvents)); it->first, it->second->m_wantedEvents));
m_polldata.erase(it); m_polldata.erase(it);
@ -324,7 +324,7 @@ int SelectLoop::remselcon(NetconP con)
return -1; return -1;
} }
LOGDEB1(("Netcon::remselcon: fd %d\n", con->m_fd)); LOGDEB1(("Netcon::remselcon: fd %d\n", con->m_fd));
map<int,NetconP>::iterator it = m_polldata.find(con->m_fd); map<int, NetconP>::iterator it = m_polldata.find(con->m_fd);
if (it == m_polldata.end()) { if (it == m_polldata.end()) {
LOGDEB1(("Netcon::remselcon: con not found for fd %d\n", con->m_fd)); LOGDEB1(("Netcon::remselcon: con not found for fd %d\n", con->m_fd));
return -1; return -1;
@ -336,7 +336,7 @@ int SelectLoop::remselcon(NetconP con)
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
// Base class (Netcon) methods // Base class (Netcon) methods
Netcon::~Netcon() Netcon::~Netcon()
{ {
closeconn(); closeconn();
if (m_peer) { if (m_peer) {
@ -369,7 +369,7 @@ void Netcon::setpeer(const char *hostname)
int Netcon::settcpnodelay(int on) int Netcon::settcpnodelay(int on)
{ {
LOGDEB2(( "Netcon::settcpnodelay\n" )); LOGDEB2(("Netcon::settcpnodelay\n"));
if (m_fd < 0) { if (m_fd < 0) {
LOGERR(("Netcon::settcpnodelay: connection not opened\n")); LOGERR(("Netcon::settcpnodelay: connection not opened\n"));
return -1; return -1;
@ -386,7 +386,7 @@ int Netcon::settcpnodelay(int on)
int Netcon::set_nonblock(int onoff) int Netcon::set_nonblock(int onoff)
{ {
int flags = fcntl(m_fd, F_GETFL, 0); int flags = fcntl(m_fd, F_GETFL, 0);
if (flags != -1 ) { if (flags != -1) {
int newflags = onoff ? flags | O_NONBLOCK : flags & ~O_NONBLOCK; int newflags = onoff ? flags | O_NONBLOCK : flags & ~O_NONBLOCK;
if (newflags != flags) if (newflags != flags)
if (fcntl(m_fd, F_SETFL, newflags) < 0) { if (fcntl(m_fd, F_SETFL, newflags) < 0) {
@ -399,7 +399,7 @@ int Netcon::set_nonblock(int onoff)
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// Data socket (NetconData) methods // Data socket (NetconData) methods
NetconData::~NetconData() NetconData::~NetconData()
{ {
freeZ(m_buf); freeZ(m_buf);
m_bufbase = 0; m_bufbase = 0;
@ -511,7 +511,7 @@ int NetconData::doreceive(char *buf, int cnt, int timeo)
LOGDEB2(("Netcon::doreceive: cnt %d, timeo %d\n", cnt, timeo)); LOGDEB2(("Netcon::doreceive: cnt %d, timeo %d\n", cnt, timeo));
cur = 0; cur = 0;
while (cnt > cur) { while (cnt > cur) {
got = receive(buf, cnt-cur, timeo); got = receive(buf, cnt - cur, timeo);
LOGDEB2(("Netcon::doreceive: got %d\n", got)); LOGDEB2(("Netcon::doreceive: got %d\n", got));
if (got < 0) { if (got < 0) {
return -1; return -1;
@ -551,7 +551,7 @@ int NetconData::getline(char *buf, int cnt, int timeo)
for (;;) { for (;;) {
// Transfer from buffer. Have to take a lot of care to keep counts and // Transfer from buffer. Have to take a lot of care to keep counts and
// pointers consistant in all end cases // pointers consistant in all end cases
int maxtransf = MIN(m_bufbytes, cnt-1); int maxtransf = MIN(m_bufbytes, cnt - 1);
int nn = maxtransf; int nn = maxtransf;
LOGDEB2(("Before loop, bufbytes %d, maxtransf %d, nn: %d\n", LOGDEB2(("Before loop, bufbytes %d, maxtransf %d, nn: %d\n",
m_bufbytes, maxtransf, nn)); m_bufbytes, maxtransf, nn));
@ -648,7 +648,7 @@ int NetconCli::openconn(const char *host, unsigned int port, int timeo)
} else { } else {
struct hostent *hp; struct hostent *hp;
if ((hp = gethostbyname(host)) == 0) { if ((hp = gethostbyname(host)) == 0) {
LOGERR(("NetconCli::openconn: gethostbyname(%s) failed\n", LOGERR(("NetconCli::openconn: gethostbyname(%s) failed\n",
host)); host));
return -1; return -1;
} }
@ -723,7 +723,7 @@ int NetconCli::openconn(const char *host, const char *serv, int timeo)
if (host[0] != '/') { if (host[0] != '/') {
struct servent *sp; struct servent *sp;
if ((sp = getservbyname(serv, "tcp")) == 0) { if ((sp = getservbyname(serv, "tcp")) == 0) {
LOGERR(("NetconCli::openconn: getservbyname failed for %s\n",serv)); LOGERR(("NetconCli::openconn: getservbyname failed for %s\n", serv));
return -1; return -1;
} }
// Callee expects the port number in host byte order // Callee expects the port number in host byte order
@ -749,7 +749,7 @@ int NetconCli::setconn(int fd)
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Methods for the main (listening) server connection // Methods for the main (listening) server connection
NetconServLis::~NetconServLis() NetconServLis::~NetconServLis()
{ {
#ifdef NETCON_ACCESSCONTROL #ifdef NETCON_ACCESSCONTROL
freeZ(okaddrs.intarray); freeZ(okaddrs.intarray);
@ -759,7 +759,7 @@ NetconServLis::~NetconServLis()
#if 0 #if 0
// code for dumping a struct servent // code for dumping a struct servent
static void dump_servent(struct servent *servp) static void dump_servent(struct servent *servp)
{ {
fprintf(stderr, "Official name %s\n", servp->s_name); fprintf(stderr, "Official name %s\n", servp->s_name);
for (char **cpp = servp->s_aliases; *cpp; cpp++) { for (char **cpp = servp->s_aliases; *cpp; cpp++) {
@ -822,7 +822,7 @@ int NetconServLis::openservice(const char *serv, int backlog)
LOGDEB1(("NetconServLis::openservice: service opened ok\n")); LOGDEB1(("NetconServLis::openservice: service opened ok\n"));
ret = 0; ret = 0;
out: out:
if (ret < 0 && m_fd >= 0) { if (ret < 0 && m_fd >= 0) {
close(m_fd); close(m_fd);
m_fd = -1; m_fd = -1;
@ -841,14 +841,14 @@ int NetconServLis::openservice(int port, int backlog)
} }
#endif #endif
int ret = -1; int ret = -1;
struct sockaddr_in ipaddr; struct sockaddr_in ipaddr;
if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
LOGSYSERR("NetconServLis", "socket", ""); LOGSYSERR("NetconServLis", "socket", "");
return -1; return -1;
} }
(void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR,(char *)&one, sizeof(one)); (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
(void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT,(char *)&one, sizeof(one)); (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT, (char *)&one, sizeof(one));
#endif /*SO_REUSEPORT*/ #endif /*SO_REUSEPORT*/
memset(&ipaddr, 0, sizeof(ipaddr)); memset(&ipaddr, 0, sizeof(ipaddr));
ipaddr.sin_family = AF_INET; ipaddr.sin_family = AF_INET;
@ -983,10 +983,10 @@ NetconServLis::accept(int timeo)
// Retrieve peer's host name. Errors are non fatal // Retrieve peer's host name. Errors are non fatal
if (m_serv.empty() || m_serv[0] != '/') { if (m_serv.empty() || m_serv[0] != '/') {
struct hostent *hp; struct hostent *hp;
if ((hp = gethostbyaddr((char *) & (who.sin_addr), if ((hp = gethostbyaddr((char *) & (who.sin_addr),
sizeof(struct in_addr), AF_INET)) == 0) { sizeof(struct in_addr), AF_INET)) == 0) {
LOGERR(("NetconServLis::accept: gethostbyaddr failed for addr 0x%lx\n", LOGERR(("NetconServLis::accept: gethostbyaddr failed for addr 0x%lx\n",
who.sin_addr.s_addr)); who.sin_addr.s_addr));
con->setpeer(inet_ntoa(who.sin_addr)); con->setpeer(inet_ntoa(who.sin_addr));
} else { } else {
con->setpeer(hp->h_name); con->setpeer(hp->h_name);
@ -1033,7 +1033,7 @@ NetconServLis::checkperms(void *cl, int)
if (i < okmasks.len) { if (i < okmasks.len) {
mask = okmasks.intarray[i]; mask = okmasks.intarray[i];
} else { } else {
mask = okmasks.intarray[okmasks.len-1]; mask = okmasks.intarray[okmasks.len - 1];
} }
LOGDEB2(("checkperms: trying okaddr 0x%x, mask 0x%x\n", LOGDEB2(("checkperms: trying okaddr 0x%x, mask 0x%x\n",
okaddrs.intarray[i], mask)); okaddrs.intarray[i], mask));
@ -1080,9 +1080,9 @@ Usage()
static int op_flags; static int op_flags;
#define OPT_MOINS 0x1 #define OPT_MOINS 0x1
#define OPT_s 0x2 /* Server */ #define OPT_s 0x2 /* Server */
#define OPT_c 0x4 /* Client */ #define OPT_c 0x4 /* Client */
#define OPT_n 0x8 /* Client sleeps forever */ #define OPT_n 0x8 /* Client sleeps forever */
extern int trycli(char *host, char *serv); extern int trycli(char *host, char *serv);
extern int tryserv(char *serv); extern int tryserv(char *serv);
@ -1118,7 +1118,7 @@ int main(int argc, char **argv)
default: default:
Usage(); Usage();
break; break;
} }
argc--; argc--;
argv++; argv++;
} }
@ -1209,8 +1209,8 @@ int trycli(char *host, char *serv)
return 1; return 1;
} }
int port = atoi(serv); int port = atoi(serv);
int ret = port > 0 ? int ret = port > 0 ?
clicon->openconn(host, port) : clicon->openconn(host, serv); clicon->openconn(host, port) : clicon->openconn(host, serv);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "openconn(%s, %s) failed\n", host, serv); fprintf(stderr, "openconn(%s, %s) failed\n", host, serv);
return 1; return 1;
@ -1271,7 +1271,7 @@ public:
} }
if (reason & Netcon::NETCONPOLL_READ) { if (reason & Netcon::NETCONPOLL_READ) {
#define LL 200 #define LL 200
char buf[LL+1]; char buf[LL + 1];
int n; int n;
if ((n = con->receive(buf, LL)) < 0) { if ((n = con->receive(buf, LL)) < 0) {
fprintf(stderr, "receive failed\n"); fprintf(stderr, "receive failed\n");
@ -1282,7 +1282,7 @@ public:
} }
buf[n] = 0; buf[n] = 0;
fprintf(stderr, "%d received \"%s\"\n", getpid(), buf); fprintf(stderr, "%d received \"%s\"\n", getpid(), buf);
con->setselevents(Netcon::NETCONPOLL_READ|Netcon::NETCONPOLL_WRITE); con->setselevents(Netcon::NETCONPOLL_READ | Netcon::NETCONPOLL_WRITE);
} }
return 0; return 0;
} }
@ -1292,7 +1292,7 @@ private:
class MyNetconServLis : public NetconServLis { class MyNetconServLis : public NetconServLis {
public: public:
MyNetconServLis(SelectLoop &loop) MyNetconServLis(SelectLoop& loop)
: NetconServLis(), m_loop(loop) { : NetconServLis(), m_loop(loop) {
} }
protected: protected:
@ -1344,8 +1344,8 @@ int tryserv(char *serv)
sigaction(SIGTERM, &sa, 0); sigaction(SIGTERM, &sa, 0);
int port = atoi(serv); int port = atoi(serv);
int ret = port > 0 ? int ret = port > 0 ?
servlis->openservice(port) : servlis->openservice(serv); servlis->openservice(port) : servlis->openservice(serv);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "openservice(%s) failed\n", serv); fprintf(stderr, "openservice(%s) failed\n", serv);
return 1; return 1;

View file

@ -36,7 +36,7 @@
/// endpoints. Netcon also has server-side static code to handle a set /// endpoints. Netcon also has server-side static code to handle a set
/// of client connections in parallel. This should be moved to a /// of client connections in parallel. This should be moved to a
/// friend class. /// friend class.
/// ///
/// The client data transfer class can also be used for /// The client data transfer class can also be used for
/// timeout-protected/asynchronous io using a given fd (ie a pipe /// timeout-protected/asynchronous io using a given fd (ie a pipe
/// descriptor) /// descriptor)
@ -48,25 +48,25 @@ class SelectLoop;
class Netcon { class Netcon {
public: public:
enum Event {NETCONPOLL_READ = 0x1, NETCONPOLL_WRITE=0x2}; enum Event {NETCONPOLL_READ = 0x1, NETCONPOLL_WRITE = 0x2};
Netcon() Netcon()
: m_peer(0), m_fd(-1), m_ownfd(true), m_didtimo(0), m_wantedEvents(0), : m_peer(0), m_fd(-1), m_ownfd(true), m_didtimo(0), m_wantedEvents(0),
m_loop(0) { m_loop(0) {
} }
virtual ~Netcon(); virtual ~Netcon();
/// Remember whom we're talking to. We let external code do this because /// Remember whom we're talking to. We let external code do this because
/// the application may have a non-dns method to find the peer name. /// the application may have a non-dns method to find the peer name.
virtual void setpeer(const char *hostname); virtual void setpeer(const char *hostname);
/// Retrieve the peer's hostname. Only works if it was set before ! /// Retrieve the peer's hostname. Only works if it was set before !
virtual const char *getpeer() { virtual const char *getpeer() {
return m_peer ? (const char *)m_peer : "none"; return m_peer ? (const char *)m_peer : "none";
} }
/// Set or reset the TCP_NODELAY option. /// Set or reset the TCP_NODELAY option.
virtual int settcpnodelay(int on = 1); virtual int settcpnodelay(int on = 1);
/// Did the last receive() call time out ? Resets the flag. /// Did the last receive() call time out ? Resets the flag.
virtual int timedout() { virtual int timedout() {
int s = m_didtimo; int s = m_didtimo;
m_didtimo = 0; m_didtimo = 0;
return s; return s;
} }
/// Return string version of last syscall error /// Return string version of last syscall error
@ -111,7 +111,7 @@ public:
static int select1(int fd, int secs, int writing = 0); static int select1(int fd, int secs, int writing = 0);
protected: protected:
char *m_peer; // Name of the connected host char *m_peer; // Name of the connected host
int m_fd; int m_fd;
bool m_ownfd; bool m_ownfd;
int m_didtimo; int m_didtimo;
@ -127,7 +127,7 @@ protected:
}; };
/// The selectloop interface is used to implement parallel servers. /// The selectloop interface is used to implement parallel servers.
// The select loop mechanism allows several netcons to be used for io // The select loop mechanism allows several netcons to be used for io
// in a program without blocking as long as there is data to be read // in a program without blocking as long as there is data to be read
// or written. In a multithread program which is also using select, it // or written. In a multithread program which is also using select, it
@ -136,13 +136,13 @@ protected:
class SelectLoop { class SelectLoop {
public: public:
SelectLoop() SelectLoop()
: m_selectloopDoReturn(false), m_selectloopReturnValue(0), : m_selectloopDoReturn(false), m_selectloopReturnValue(0),
m_placetostart(0), m_placetostart(0),
m_periodichandler(0), m_periodicparam(0), m_periodicmillis(0) { m_periodichandler(0), m_periodicparam(0), m_periodicmillis(0) {
} }
/// Loop waiting for events on the connections and call the /// Loop waiting for events on the connections and call the
/// cando() method on the object when something happens (this will in /// cando() method on the object when something happens (this will in
/// turn typically call the app callback set on the netcon). Possibly /// turn typically call the app callback set on the netcon). Possibly
/// call the periodic handler (if set) at regular intervals. /// call the periodic handler (if set) at regular intervals.
/// @return -1 for error. 0 if no descriptors left for i/o. 1 for periodic /// @return -1 for error. 0 if no descriptors left for i/o. 1 for periodic
@ -151,8 +151,8 @@ public:
/// Call from data handler: make selectloop return the param value /// Call from data handler: make selectloop return the param value
void loopReturn(int value) { void loopReturn(int value) {
m_selectloopDoReturn = true; m_selectloopDoReturn = true;
m_selectloopReturnValue = value; m_selectloopReturnValue = value;
} }
/// Add a connection to be monitored (this will usually be called /// Add a connection to be monitored (this will usually be called
/// from the server's listen connection's accept callback) /// from the server's listen connection's accept callback)
@ -162,11 +162,11 @@ public:
int remselcon(NetconP con); int remselcon(NetconP con);
/// Set a function to be called periodically, or a time before return. /// Set a function to be called periodically, or a time before return.
/// @param handler the function to be called. /// @param handler the function to be called.
/// - if it is 0, selectloop() will return after ms mS (and can be called /// - if it is 0, selectloop() will return after ms mS (and can be called
/// again /// again
/// - if it is not 0, it will be called at ms mS intervals. If its return /// - if it is not 0, it will be called at ms mS intervals. If its return
/// value is <= 0, selectloop will return. /// value is <= 0, selectloop will return.
/// @param clp client data to be passed to handler at every call. /// @param clp client data to be passed to handler at every call.
/// @param ms milliseconds interval between handler calls or /// @param ms milliseconds interval between handler calls or
/// before return. Set to 0 for no periodic handler. /// before return. Set to 0 for no periodic handler.
@ -195,12 +195,12 @@ private:
/////////////////////// ///////////////////////
class NetconData; class NetconData;
/// Class for the application callback routine (when in selectloop). /// Class for the application callback routine (when in selectloop).
/// ///
/// This is set by the app on the NetconData by calling /// This is set by the app on the NetconData by calling
/// setcallback(). It is then called from the NetconData's cando() /// setcallback(). It is then called from the NetconData's cando()
/// routine, itself called by selectloop. /// routine, itself called by selectloop.
/// ///
/// It would be nicer to override cando() in a subclass instead of /// It would be nicer to override cando() in a subclass instead of
/// setting a callback, but this can't be done conveniently because /// setting a callback, but this can't be done conveniently because
/// accept() always creates a base NetconData (another approach would /// accept() always creates a base NetconData (another approach would
@ -229,7 +229,7 @@ public:
/// Read from the connection /// Read from the connection
/// @param buf the data buffer /// @param buf the data buffer
/// @param cnt the number of bytes we should try to read (but we return /// @param cnt the number of bytes we should try to read (but we return
/// as soon as we get data) /// as soon as we get data)
/// @param timeo maximum number of seconds we should be waiting for data. /// @param timeo maximum number of seconds we should be waiting for data.
/// @return the count of bytes actually read. 0 for timeout (call /// @return the count of bytes actually read. 0 for timeout (call
@ -251,16 +251,16 @@ public:
} }
private: private:
char *m_buf; // Buffer. Only used when doing getline()s char *m_buf; // Buffer. Only used when doing getline()s
char *m_bufbase; // Pointer to current 1st byte of useful data char *m_bufbase; // Pointer to current 1st byte of useful data
int m_bufbytes; // Bytes of data. int m_bufbytes; // Bytes of data.
int m_bufsize; // Total buffer size int m_bufsize; // Total buffer size
STD_SHARED_PTR<NetconWorker> m_user; STD_SHARED_PTR<NetconWorker> m_user;
virtual int cando(Netcon::Event reason); // Selectloop slot virtual int cando(Netcon::Event reason); // Selectloop slot
}; };
/// Network endpoint, client side. /// Network endpoint, client side.
class NetconCli : public NetconData { class NetconCli : public NetconData {
public: public:
NetconCli(int silent = 0) { NetconCli(int silent = 0) {
m_silentconnectfailure = silent; m_silentconnectfailure = silent;
@ -276,7 +276,7 @@ public:
/// AF_UNIX service. serv is ignored in this case. /// AF_UNIX service. serv is ignored in this case.
int openconn(const char *host, unsigned int port, int timeo = -1); int openconn(const char *host, unsigned int port, int timeo = -1);
/// Reuse existing fd. /// Reuse existing fd.
/// We DONT take ownership of the fd, and do no closin' EVEN on an /// We DONT take ownership of the fd, and do no closin' EVEN on an
/// explicit closeconn() or setconn() (use getfd(), close, /// explicit closeconn() or setconn() (use getfd(), close,
/// setconn(-1) if you need to really close the fd and have no /// setconn(-1) if you need to really close the fd and have no
@ -289,7 +289,7 @@ public:
} }
private: private:
int m_silentconnectfailure; // No logging of connection failures if set int m_silentconnectfailure; // No logging of connection failures if set
}; };
class NetconServCon; class NetconServCon;
@ -316,9 +316,9 @@ class NetconServLis : public Netcon {
public: public:
NetconServLis() { NetconServLis() {
#ifdef NETCON_ACCESSCONTROL #ifdef NETCON_ACCESSCONTROL
permsinit = 0; permsinit = 0;
okaddrs.len = okmasks.len = 0; okaddrs.len = okmasks.len = 0;
okaddrs.intarray = okmasks.intarray = 0; okaddrs.intarray = okmasks.intarray = 0;
#endif /* NETCON_ACCESSCONTROL */ #endif /* NETCON_ACCESSCONTROL */
} }
~NetconServLis(); ~NetconServLis();
@ -327,7 +327,7 @@ public:
int openservice(const char *serv, int backlog = 10); int openservice(const char *serv, int backlog = 10);
/// Open service by port number. /// Open service by port number.
int openservice(int port, int backlog = 10); int openservice(int port, int backlog = 10);
/// Wait for incoming connection. Returned connected Netcon /// Wait for incoming connection. Returned connected Netcon
NetconServCon *accept(int timeo = -1); NetconServCon *accept(int timeo = -1);
protected: protected:
@ -337,7 +337,7 @@ protected:
virtual int cando(Netcon::Event reason); virtual int cando(Netcon::Event reason);
// Empty if port was numeric, else service name or socket path // Empty if port was numeric, else service name or socket path
std::string m_serv; std::string m_serv;
private: private:
#ifdef NETCON_ACCESSCONTROL #ifdef NETCON_ACCESSCONTROL
@ -353,16 +353,16 @@ private:
/// Server-side accepted client connection. The only specific code /// Server-side accepted client connection. The only specific code
/// allows closing the listening endpoint in the child process (in the /// allows closing the listening endpoint in the child process (in the
/// case of a forking server) /// case of a forking server)
class NetconServCon : public NetconData { class NetconServCon : public NetconData {
public: public:
NetconServCon(int newfd, Netcon* lis = 0) { NetconServCon(int newfd, Netcon* lis = 0) {
m_liscon = lis; m_liscon = lis;
m_fd = newfd; m_fd = newfd;
} }
/// This is for forked servers that want to get rid of the main socket /// This is for forked servers that want to get rid of the main socket
void closeLisCon() { void closeLisCon() {
if (m_liscon) { if (m_liscon) {
m_liscon->closeconn(); m_liscon->closeconn();
} }
} }
private: private:

View file

@ -22,12 +22,16 @@
#endif #endif
#include <errno.h> #include <errno.h>
#include "safefcntl.h"
#include <sys/types.h> #include <sys/types.h>
#ifdef _WIN32
#include "safefcntl.h"
#include "safesysstat.h" #include "safesysstat.h"
#include "safeunistd.h" #include "safeunistd.h"
#ifndef _WIN32 #else
#define O_BINARY 0 #define O_BINARY 0
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#endif #endif
#include <string> #include <string>
@ -41,33 +45,34 @@ public:
FileToString(string& data) : m_data(data) {} FileToString(string& data) : m_data(data) {}
string& m_data; string& m_data;
bool init(size_t size, string *reason) { bool init(size_t size, string *reason) {
if (size > 0) if (size > 0) {
m_data.reserve(size); m_data.reserve(size);
return true; }
return true;
} }
bool data(const char *buf, int cnt, string *reason) { bool data(const char *buf, int cnt, string *reason) {
try { try {
m_data.append(buf, cnt); m_data.append(buf, cnt);
} catch (...) { } catch (...) {
catstrerror(reason, "append", errno); catstrerror(reason, "append", errno);
return false; return false;
} }
return true; return true;
} }
}; };
bool file_to_string(const string &fn, string &data, string *reason) bool file_to_string(const string& fn, string& data, string *reason)
{ {
return file_to_string(fn, data, 0, size_t(-1), reason); return file_to_string(fn, data, 0, size_t(-1), reason);
} }
bool file_to_string(const string &fn, string &data, off_t offs, size_t cnt, bool file_to_string(const string& fn, string& data, off_t offs, size_t cnt,
string *reason) string *reason)
{ {
FileToString accum(data); FileToString accum(data);
return file_scan(fn, &accum, offs, cnt, reason); return file_scan(fn, &accum, offs, cnt, reason);
} }
bool file_scan(const string &fn, FileScanDo* doer, string *reason) bool file_scan(const string& fn, FileScanDo* doer, string *reason)
{ {
return file_scan(fn, doer, 0, size_t(-1), reason); return file_scan(fn, doer, 0, size_t(-1), reason);
} }
@ -77,29 +82,29 @@ const int RDBUFSZ = 8192;
// on both linux i586 and macosx (compared to just append()) // on both linux i586 and macosx (compared to just append())
// Also tried a version with mmap, but it's actually slower on the mac and not // Also tried a version with mmap, but it's actually slower on the mac and not
// faster on linux. // faster on linux.
bool file_scan(const string &fn, FileScanDo* doer, off_t startoffs, bool file_scan(const string& fn, FileScanDo* doer, off_t startoffs,
size_t cnttoread, string *reason) size_t cnttoread, string *reason)
{ {
if (startoffs < 0) { if (startoffs < 0) {
*reason += " file_scan: negative startoffs not allowed"; *reason += " file_scan: negative startoffs not allowed";
return false; return false;
} }
bool ret = false; bool ret = false;
bool noclosing = true; bool noclosing = true;
int fd = 0; int fd = 0;
struct stat st; struct stat st;
// Initialize st_size: if fn.empty() , the fstat() call won't happen. // Initialize st_size: if fn.empty() , the fstat() call won't happen.
st.st_size = 0; st.st_size = 0;
// If we have a file name, open it, else use stdin. // If we have a file name, open it, else use stdin.
if (!fn.empty()) { if (!fn.empty()) {
fd = open(fn.c_str(), O_RDONLY|O_BINARY); fd = open(fn.c_str(), O_RDONLY | O_BINARY);
if (fd < 0 || fstat(fd, &st) < 0) { if (fd < 0 || fstat(fd, &st) < 0) {
catstrerror(reason, "open/stat", errno); catstrerror(reason, "open/stat", errno);
return false; return false;
} }
noclosing = false; noclosing = false;
} }
#if defined O_NOATIME && O_NOATIME != 0 #if defined O_NOATIME && O_NOATIME != 0
@ -108,12 +113,12 @@ bool file_scan(const string &fn, FileScanDo* doer, off_t startoffs,
} }
#endif #endif
if (cnttoread != (size_t)-1 && cnttoread) { if (cnttoread != (size_t) - 1 && cnttoread) {
doer->init(cnttoread+1, reason); doer->init(cnttoread + 1, reason);
} else if (st.st_size > 0) { } else if (st.st_size > 0) {
doer->init(size_t(st.st_size+1), reason); doer->init(size_t(st.st_size + 1), reason);
} else { } else {
doer->init(0, reason); doer->init(0, reason);
} }
off_t curoffs = 0; off_t curoffs = 0;
@ -130,36 +135,40 @@ bool file_scan(const string &fn, FileScanDo* doer, off_t startoffs,
for (;;) { for (;;) {
size_t toread = RDBUFSZ; size_t toread = RDBUFSZ;
if (startoffs > 0 && curoffs < startoffs) { if (startoffs > 0 && curoffs < startoffs) {
toread = size_t(MIN(RDBUFSZ, startoffs - curoffs)); toread = size_t(MIN(RDBUFSZ, startoffs - curoffs));
} }
if (cnttoread != size_t(-1)) { if (cnttoread != size_t(-1)) {
toread = MIN(toread, cnttoread - totread); toread = MIN(toread, cnttoread - totread);
} }
ssize_t n = static_cast<ssize_t>(read(fd, buf, toread)); ssize_t n = static_cast<ssize_t>(read(fd, buf, toread));
if (n < 0) { if (n < 0) {
catstrerror(reason, "read", errno); catstrerror(reason, "read", errno);
goto out; goto out;
} }
if (n == 0) if (n == 0) {
break; break;
}
curoffs += n; curoffs += n;
if (curoffs - n < startoffs) if (curoffs - n < startoffs) {
continue; continue;
}
if (!doer->data(buf, n, reason)) {
goto out; if (!doer->data(buf, n, reason)) {
} goto out;
}
totread += n; totread += n;
if (cnttoread > 0 && totread >= cnttoread) if (cnttoread > 0 && totread >= cnttoread) {
break; break;
}
} }
ret = true; ret = true;
out: out:
if (fd >= 0 && !noclosing) if (fd >= 0 && !noclosing) {
close(fd); close(fd);
}
return ret; return ret;
} }
@ -181,29 +190,28 @@ using namespace std;
using namespace std; using namespace std;
class myCB : public FsTreeWalkerCB { class myCB : public FsTreeWalkerCB {
public: public:
FsTreeWalker::Status processone(const string &path, FsTreeWalker::Status processone(const string& path,
const struct stat *st, const struct stat *st,
FsTreeWalker::CbFlag flg) FsTreeWalker::CbFlag flg) {
{ if (flg == FsTreeWalker::FtwDirEnter) {
if (flg == FsTreeWalker::FtwDirEnter) { //cout << "[Entering " << path << "]" << endl;
//cout << "[Entering " << path << "]" << endl; } else if (flg == FsTreeWalker::FtwDirReturn) {
} else if (flg == FsTreeWalker::FtwDirReturn) { //cout << "[Returning to " << path << "]" << endl;
//cout << "[Returning to " << path << "]" << endl; } else if (flg == FsTreeWalker::FtwRegular) {
} else if (flg == FsTreeWalker::FtwRegular) { //cout << path << endl;
//cout << path << endl; string s, reason;
string s, reason; if (!file_to_string(path, s, &reason)) {
if (!file_to_string(path, s, &reason)) { cerr << "Failed: " << reason << " : " << path << endl;
cerr << "Failed: " << reason << " : " << path << endl; } else {
} else { //cout <<
//cout << //"================================================" << endl;
//"================================================" << endl; cout << path << endl;
cout << path << endl; // cout << s;
// cout << s; }
} reason.clear();
reason.clear(); }
} return FsTreeWalker::FtwOk;
return FsTreeWalker::FtwOk;
} }
}; };
@ -214,8 +222,8 @@ static int op_flags;
static const char *thisprog; static const char *thisprog;
static char usage [] = static char usage [] =
"trreadfile [-o offs] [-c cnt] topdirorfile\n\n" "trreadfile [-o offs] [-c cnt] topdirorfile\n\n"
; ;
static void static void
Usage(void) Usage(void)
{ {
@ -228,51 +236,71 @@ int main(int argc, const char **argv)
off_t offs = 0; off_t offs = 0;
size_t cnt = size_t(-1); size_t cnt = size_t(-1);
thisprog = argv[0]; thisprog = argv[0];
argc--; argv++; argc--;
argv++;
while (argc > 0 && **argv == '-') { while (argc > 0 && **argv == '-') {
(*argv)++; (*argv)++;
if (!(**argv)) if (!(**argv))
/* Cas du "adb - core" */ /* Cas du "adb - core" */
Usage(); {
while (**argv) Usage();
switch (*(*argv)++) { }
case 'c': op_flags |= OPT_c; if (argc < 2) Usage(); while (**argv)
cnt = atoll(*(++argv)); argc--; switch (*(*argv)++) {
goto b1; case 'c':
case 'o': op_flags |= OPT_o; if (argc < 2) Usage(); op_flags |= OPT_c;
offs = strtoull(*(++argv), 0, 0); argc--; if (argc < 2) {
goto b1; Usage();
default: Usage(); break; }
} cnt = atoll(*(++argv));
b1: argc--; argv++; argc--;
} goto b1;
case 'o':
op_flags |= OPT_o;
if (argc < 2) {
Usage();
}
offs = strtoull(*(++argv), 0, 0);
argc--;
goto b1;
default:
Usage();
break;
}
b1:
argc--;
argv++;
}
if (argc != 1) if (argc != 1) {
Usage(); Usage();
string top = *argv++;argc--; }
cerr << "filename " << top << " offs " << offs << " cnt " << cnt << endl; string top = *argv++;
argc--;
cerr << "filename " << top << " offs " << offs << " cnt " << cnt << endl;
struct stat st; struct stat st;
if (!top.empty() && stat(top.c_str(), &st) < 0) { if (!top.empty() && stat(top.c_str(), &st) < 0) {
perror("stat"); perror("stat");
exit(1); exit(1);
} }
if (!top.empty() && S_ISDIR(st.st_mode)) { if (!top.empty() && S_ISDIR(st.st_mode)) {
FsTreeWalker walker; FsTreeWalker walker;
myCB cb; myCB cb;
walker.walk(top, cb); walker.walk(top, cb);
if (walker.getErrCnt() > 0) if (walker.getErrCnt() > 0) {
cout << walker.getReason(); cout << walker.getReason();
} else { }
string s, reason; } else {
if (!file_to_string(top, s, offs, cnt, &reason)) { string s, reason;
cerr << reason << endl; if (!file_to_string(top, s, offs, cnt, &reason)) {
exit(1); cerr << reason << endl;
} else { exit(1);
cout << s; } else {
} cout << s;
} }
exit(0); }
exit(0);
} }
#endif //TEST_READFILE #endif //TEST_READFILE

View file

@ -21,8 +21,8 @@
#include <string> #include <string>
/** /**
* Read file in chunks, calling an accumulator for each chunk. Can be used * Read file in chunks, calling an accumulator for each chunk. Can be used
* for reading in a file, computing an md5... * for reading in a file, computing an md5...
*/ */
class FileScanDo { class FileScanDo {
@ -31,20 +31,20 @@ public:
virtual bool init(size_t size, std::string *reason) = 0; virtual bool init(size_t size, std::string *reason) = 0;
virtual bool data(const char *buf, int cnt, std::string* reason) = 0; virtual bool data(const char *buf, int cnt, std::string* reason) = 0;
}; };
bool file_scan(const std::string &filename, FileScanDo* doer, std::string *reason = 0); bool file_scan(const std::string& filename, FileScanDo* doer, std::string *reason = 0);
/* Same but only process count cnt from offset offs. Set cnt to size_t(-1) /* Same but only process count cnt from offset offs. Set cnt to size_t(-1)
* for no limit */ * for no limit */
bool file_scan(const std::string &fn, FileScanDo* doer, off_t offs, size_t cnt, bool file_scan(const std::string& fn, FileScanDo* doer, off_t offs, size_t cnt,
std::string *reason = 0); std::string *reason = 0);
/** /**
* Read file into string. * Read file into string.
* @return true for ok, false else * @return true for ok, false else
*/ */
bool file_to_string(const std::string &filename, std::string &data, std::string *reason = 0); bool file_to_string(const std::string& filename, std::string& data, std::string *reason = 0);
/** Read file chunk into string. Set cnt to size_t(-1) for whole file */ /** Read file chunk into string. Set cnt to size_t(-1) for whole file */
bool file_to_string(const std::string &filename, std::string &data, bool file_to_string(const std::string& filename, std::string& data,
off_t offs, size_t cnt, std::string *reason = 0); off_t offs, size_t cnt, std::string *reason = 0);
#endif /* _READFILE_H_INCLUDED_ */ #endif /* _READFILE_H_INCLUDED_ */