├── .dep.inc ├── .gitignore ├── CdnRefreshInterface.cpp ├── CdnRefreshInterface.h ├── CommandInterface.cpp ├── CommandInterface.h ├── Daemon.h ├── FileSynchronize.cpp ├── FileSynchronize.h ├── HttpInterface.cpp ├── HttpInterface.h ├── Initialize.cpp ├── Initialize.h ├── Inotify.cpp ├── Inotify.h ├── Interface.cpp ├── Interface.h ├── Makefile ├── QueueFilter.cpp ├── QueueFilter.h ├── QueueRetry.cpp ├── QueueRetry.h ├── README.md ├── SocketInterface.cpp ├── SocketInterface.h ├── build └── .gitkeep ├── dist └── Release │ └── GNU-Linux-x86 │ └── confxml.xml ├── main.cpp ├── main.h ├── nbproject ├── Makefile-Release.mk ├── Makefile-impl.mk ├── Makefile-variables.mk ├── Package-Release.bash ├── configurations.xml ├── private │ ├── configurations.xml │ ├── private.properties │ └── private.xml ├── project.properties └── project.xml ├── readme-detail.md ├── xmlParser.cpp └── xmlParser.h /.dep.inc: -------------------------------------------------------------------------------- 1 | # This code depends on make tool being used 2 | DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES})) 3 | ifneq (${DEPFILES},) 4 | include ${DEPFILES} 5 | endif 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/GNU-Linux-x86/sersync 2 | dist/GNU-Linux-x86/sersync2 3 | *.o 4 | *.o.d -------------------------------------------------------------------------------- /CdnRefreshInterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "xmlParser.h" 24 | #include "CdnRefreshInterface.h" 25 | #include "Initialize.h" 26 | #include"main.h" 27 | 28 | #define MAXDATASIZE 4096 /*the maximum number of every transfer*/ 29 | 30 | CdnRefreshInterface::CdnRefreshInterface() 31 | { 32 | //设定超时重传时间 33 | timeout.tv_sec = 0; 34 | timeout.tv_usec = 300000; 35 | } 36 | 37 | void CdnRefreshInterface::XmlParse(std::string config_file_name) 38 | { 39 | XMLNode xMainNode = XMLNode::openFileHelper( config_file_name.c_str( ), "head" ); 40 | int num = xMainNode.nChildNode( "plugin" ); 41 | if (num == 0) 42 | { 43 | perror( "错误,配置文件中没有插件标签\n" ); 44 | exit( 1 ); 45 | } 46 | for (int i = 0; i < num; i++) 47 | { 48 | string name = xMainNode.getChildNode( "plugin", i ).getAttribute( "name" ); 49 | if (name != "refreshCDN") 50 | { 51 | continue; 52 | } 53 | XMLNode xNode = xMainNode.getChildNode( "plugin", i ); 54 | XMLNode lNode = xNode.getChildNode( "localpath", 0 ); 55 | int wtchnum = xNode.nChildNode( "localpath" ); //get the numbers of the localpath tag 56 | if (wtchnum == 0) 57 | { 58 | perror( "错误,配置文件中没有指明需要监控的路径\n" ); 59 | exit( 1 ); 60 | } 61 | m_watch = lNode.getAttribute( "watch" ); 62 | m_watch = Initialize::SplitLastSlash( m_watch ); 63 | 64 | XMLNode Node = lNode.getChildNode( "cdninfo" ); 65 | m_domainName = Node.getAttribute( "domainname" ); 66 | m_port = atoi( Node.getAttribute( "port" ) ); 67 | m_username = Node.getAttribute( "username" ); 68 | m_passwd = Node.getAttribute( "passwd" ); 69 | 70 | Node = lNode.getChildNode( "sendurl" ); 71 | m_urlBase = Node.getAttribute( "base" ); 72 | 73 | Node = lNode.getChildNode( "regexurl" ); 74 | m_urlRegex = Node.getAttribute( "match" ); 75 | m_regexFlag = Node.getAttribute( "regex" ); //if =='false'will ignore the regex match; 76 | 77 | if (m_domainName.empty( ) || m_username.empty( ) || m_passwd.empty( ) || \ 78 | m_urlBase.empty( ) || m_regexFlag.empty( ) || m_urlRegex.empty( ) \ 79 | ) 80 | { 81 | 82 | cout << "配置文件错误,一些xml属性不应为空,请检查" << endl; 83 | exit( 1 ); 84 | } 85 | break; 86 | } 87 | 88 | 89 | } 90 | 91 | int CdnRefreshInterface::Execute(Event e) 92 | { 93 | 94 | string fullPath = PackagePath( e ); 95 | if(fullPath.empty()) 96 | { 97 | return 1; 98 | } 99 | if (debug_level & SUB_CLASS) 100 | { 101 | cout << fullPath << endl; 102 | } 103 | 104 | char buf[MAXDATASIZE] = "\0";//receive date from cdn 105 | int sockfd, recvbytes, flags, n; 106 | struct sockaddr_in serv_addr; 107 | struct hostent *host; 108 | 109 | if ((host = gethostbyname( m_domainName.c_str() )) == NULL) 110 | { 111 | printf( "get host by name error ccms.chinacache.com down" ); 112 | return 1; 113 | } 114 | 115 | serv_addr.sin_family = AF_INET; 116 | serv_addr.sin_port = htons( m_port ); 117 | serv_addr.sin_addr = *((struct in_addr *) host->h_addr); 118 | bzero( &(serv_addr.sin_zero), 8 ); 119 | 120 | if ((sockfd = socket( AF_INET, SOCK_STREAM, 0 )) == -1) 121 | { 122 | return 1; 123 | } 124 | flags = fcntl( sockfd, F_GETFL, 0 ); 125 | fcntl( sockfd, F_SETFL, flags | O_NONBLOCK ); 126 | 127 | connect( sockfd, (struct sockaddr *) & serv_addr, sizeof (struct sockaddr) ); 128 | 129 | fd_set rset, wset; 130 | FD_ZERO( &wset ); 131 | FD_SET( sockfd, &wset ); 132 | 133 | if ((n = select( sockfd + 1, NULL, &wset, NULL, &timeout )) <= 0) 134 | { 135 | if (debug_level & SUB_CLASS) 136 | { 137 | printf( "time out connect error" ); 138 | } 139 | close( sockfd ); 140 | return 1; 141 | } 142 | 143 | if (send( sockfd, fullPath.c_str( ), strlen( fullPath.c_str( ) ), 0 ) == -1) 144 | { 145 | if (debug_level & SUB_CLASS) 146 | { 147 | cout << "send error!"; 148 | } 149 | close( sockfd ); 150 | return 1; 151 | } 152 | 153 | //需要重置,因为之前select之后,会对相应的rset置为非0 154 | FD_ZERO( &rset ); 155 | FD_SET( sockfd, &rset ); 156 | if ((n = select( sockfd + 1, &rset, NULL, NULL, &timeout )) <= 0) 157 | { 158 | if (debug_level & SUB_CLASS) 159 | { 160 | printf( "time out connect error" ); 161 | } 162 | close( sockfd ); 163 | return 1; 164 | } 165 | recvbytes = recv( sockfd, buf, MAXDATASIZE, 0 ); 166 | if (0 == recvbytes || -1 == recvbytes) 167 | { 168 | if (debug_level & SUB_CLASS) 169 | { 170 | cout << "this recvbytes==0" << endl; 171 | } 172 | close( sockfd ); 173 | return 1; 174 | } 175 | 176 | ErrorLog( buf ,fullPath); 177 | close( sockfd ); 178 | if (debug_level & SUB_CLASS) 179 | { 180 | printf( "Received: %s\n", buf ); /*从返回结果读取 whatsup信息,如果为succeed说明提交成功,另超量提交类似获取 */ 181 | } 182 | return 0; 183 | 184 | } 185 | 186 | string CdnRefreshInterface::PackagePath(Event e) 187 | { 188 | string temp; 189 | char buf[1024] = "\0"; 190 | string fullPath; 191 | fullPath += m_urlBase; 192 | if (m_regexFlag == "true") 193 | { 194 | boost::smatch what; 195 | boost::regex expression( m_urlRegex ); 196 | int res = boost::regex_search( e->path, what, expression ); 197 | if (false == res || what.size( ) <= 0) 198 | { 199 | return temp; 200 | } 201 | fullPath += what[1]; 202 | fullPath += what.suffix( ); 203 | } else 204 | { 205 | string tp = (e->path).substr( m_watch.size( ), (e->path).size( ) - m_watch.size( ) ); 206 | fullPath += tp; 207 | } 208 | 209 | if (e->dir) 210 | { 211 | fullPath = "&dirs=" + fullPath + "/"; 212 | } else 213 | { 214 | fullPath = "&urls=" + fullPath; 215 | } 216 | snprintf( buf, sizeof (buf), \ 217 | "HEAD /index.jsp?user=%s&pswd=%s&ok=ok%s HTTP/1.0\r\nHost: %s\r\n\r\n", \ 218 | m_username.c_str( ), m_passwd.c_str( ), fullPath.c_str( ), m_domainName.c_str( ) ); 219 | 220 | temp = buf; 221 | return temp; 222 | } 223 | 224 | void CdnRefreshInterface::ErrorLog(string temp,string fullPath) 225 | { 226 | static int iWriteTime = 0; 227 | if (iWriteTime < 1000) 228 | { 229 | m_fout.open( "errorlog.txt", ofstream::app ); 230 | iWriteTime++; 231 | } else 232 | { 233 | m_fout.open( "errorlog.txt", ofstream::out ); 234 | iWriteTime = 0; 235 | } 236 | int pos = 0; 237 | if ((pos = (temp.find( "\n", temp.find( "whatsup" ) ))) != string::npos) 238 | { 239 | int i = pos - 1 - temp.find( "whatsup" ); 240 | temp = temp.substr( temp.find( "whatsup" ), i ); 241 | } 242 | 243 | m_fout << temp << " " << fullPath << "\n"; 244 | m_fout.close( ); 245 | } 246 | 247 | -------------------------------------------------------------------------------- /CdnRefreshInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: CdnRefresh.h 3 | * Author: 周洋 JohnTech 4 | * 5 | * Created on 2010年1月19日, 下午3:29 6 | */ 7 | #ifndef _CDNREFRESH_H 8 | #define _CDNREFRESH_H 9 | #include "Inotify.h" 10 | #include"Interface.h" 11 | #include 12 | using namespace std; 13 | 14 | class CdnRefreshInterface : public Interface { 15 | public: 16 | string m_domainName; 17 | string m_username; 18 | string m_passwd; 19 | string m_regexFlag; //if == 'false' will ignore the regex match; 20 | string m_urlBase; 21 | string m_urlRegex; 22 | ofstream m_fout; 23 | public: 24 | CdnRefreshInterface(); 25 | virtual void XmlParse(std::string xml = "confxml.xml"); 26 | virtual int Execute(Event e); 27 | protected: 28 | string PackagePath(Event e); 29 | void ErrorLog(std::string temp,std::string fullPath); 30 | }; 31 | 32 | #endif /* _CDNREFRESH_H */ 33 | 34 | -------------------------------------------------------------------------------- /CommandInterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "QueueFilter.h" 3 | #include "Initialize.h" 4 | #include "xmlParser.h" 5 | #include "CommandInterface.h" 6 | 7 | CommandInterface::CommandInterface() : m_prefix("/bin/sh"), m_ignoreError(false), m_debug(0), m_filter(false) 8 | { 9 | 10 | } 11 | 12 | void CommandInterface::XmlParse(std::string config_file_name) 13 | { 14 | XMLNode xMainNode = XMLNode::openFileHelper(config_file_name.c_str(), "head"); 15 | string tmp = xMainNode.getChildNode("debug").getAttribute("start"); 16 | if (tmp == "true") m_debug = 1; 17 | XMLNode Node = xMainNode.getChildNode("sersync"); 18 | m_watch = Node.getChildNode("localpath").getAttribute("watch"); 19 | int num = xMainNode.nChildNode("plugin"); 20 | if (num == 0) 21 | { 22 | perror("Error there is no plugin tag in config xml\n"); 23 | exit(1); 24 | } 25 | for (int i = 0; i < num; i++) 26 | { 27 | string name = xMainNode.getChildNode("plugin", i).getAttribute("name"); 28 | if (name == "command") 29 | { 30 | XMLNode xNode = xMainNode.getChildNode("plugin", i); 31 | m_prefix = xNode.getChildNode("param").getAttribute("prefix"); 32 | m_suffix = xNode.getChildNode("param").getAttribute("suffix"); 33 | string tmp = xNode.getChildNode("param").getAttribute("ignoreError"); 34 | if (tmp == "true") 35 | { 36 | m_ignoreError = true; 37 | } 38 | tmp = xNode.getChildNode("filter").getAttribute("start"); 39 | if (tmp == "true") 40 | { 41 | m_filter = true; 42 | int num = xNode.getChildNode("filter").nChildNode("include"); 43 | for (int i = 0; i < num; i++) 44 | { 45 | string t = xNode.getChildNode("filter").getChildNode(i).getAttribute("expression"); 46 | try 47 | { 48 | ptrRegex tmp(new boost::regex(t)); 49 | pattern.push_back(tmp); 50 | 51 | } catch (boost::regex_error& e) 52 | { 53 | cout << "plugin command regular expression error" << endl; 54 | cout << "error information is:\t" << e.what() << endl; 55 | exit(1); 56 | } 57 | } 58 | } 59 | break; 60 | } 61 | } 62 | 63 | } 64 | 65 | int CommandInterface::Execute(Event e) 66 | { 67 | //if (e->operation == 0 || e->mask==256) return 0; 68 | boost::cmatch what; 69 | if (m_filter == true) 70 | { 71 | int valid = 0; 72 | for (int i = 0; i < pattern.size(); i++) 73 | { 74 | if (boost::regex_match((e->path).c_str(), what, *(pattern[i]))) 75 | { 76 | //cout<<"this is other"<path + " " + m_suffix; 84 | if (m_ignoreError == true) 85 | { 86 | command += " >/dev/null 2>&1 "; 87 | } 88 | if (m_debug == 1) 89 | { 90 | cout << command << endl; 91 | } 92 | system(command.c_str()); 93 | } 94 | -------------------------------------------------------------------------------- /CommandInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: CommandInterface.h 3 | * Author: root 4 | * 5 | * Created on 2010年5月13日, 下午8:47 6 | */ 7 | 8 | #ifndef _COMMANDINTERFACE_H 9 | #define _COMMANDINTERFACE_H 10 | #include "Interface.h" 11 | #include "Inotify.h" 12 | 13 | class CommandInterface : public Interface { 14 | public: 15 | std::string m_prefix; 16 | std::string m_suffix; 17 | std::vector pattern; 18 | bool m_ignoreError; 19 | int m_debug; 20 | bool m_filter; 21 | public: 22 | CommandInterface(); 23 | virtual void XmlParse(std::string config_file_name); 24 | virtual int Execute(Event e); 25 | }; 26 | 27 | #endif /* _COMMANDINTERFACE_H */ 28 | 29 | -------------------------------------------------------------------------------- /Daemon.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef _DAEMON_H 11 | #define _DAEMON_H 12 | 13 | void Daemon_Stop(int nSignal) { 14 | pid_t _pid_t; 15 | int nState; 16 | while ((_pid_t = waitpid(-1, &nState, WNOHANG)) > 0); 17 | signal(SIGCLD, Daemon_Stop); 18 | } 19 | 20 | int Daemon_Start() { 21 | std::cout << "daemon start,sersync run behind the console " << std::endl; 22 | pid_t pid1; 23 | assert((pid1 = fork()) >= 0); 24 | if (pid1 != 0) { 25 | sleep(1); 26 | exit(0); 27 | } 28 | assert(setsid() >= 0); 29 | umask(0); 30 | signal(SIGINT, SIG_IGN); 31 | signal(SIGCLD, Daemon_Stop); 32 | return 0; 33 | } 34 | #endif 35 | -------------------------------------------------------------------------------- /FileSynchronize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "QueueFilter.h" 18 | #include "Inotify.h" 19 | #include "xmlParser.h" 20 | #include "QueueRetry.h" 21 | #include "FileSynchronize.h" 22 | #include "Initialize.h" 23 | #include "main.h" 24 | #include "SocketInterface.h" 25 | //#include "HttpInterface.h" 26 | #include "CommandInterface.h" 27 | #include "CdnRefreshInterface.h" 28 | 29 | //#define SCRIPT_EXECUTE_INTERVAL 3600 //每隔1个小时执行一次rsync_fail_log.sh 30 | 31 | extern int IN_SYNC; 32 | 33 | int FileSynchronize::sleep_group = 0; //用于统计当前处于sleep状态的线程数量 34 | 35 | boost::condition_variable FileSynchronize::work_cond; //工作条件对象 36 | 37 | boost::mutex FileSynchronize::worklock; //用于统计工作线程数量的锁定 38 | 39 | int FileSynchronize::m_crontab = -1; //默认不开起crontab进行整体推送 40 | 41 | string FileSynchronize::m_users = ""; 42 | 43 | string FileSynchronize::m_delete = ""; 44 | 45 | string FileSynchronize::m_password = ""; 46 | 47 | bool FileSynchronize::debug = false; 48 | 49 | std::string FileSynchronize::ssh = ""; 50 | 51 | std::string FileSynchronize::port = ""; 52 | 53 | std::string FileSynchronize::timeout = ""; 54 | 55 | std::string FileSynchronize::watch; 56 | 57 | std::string FileSynchronize::m_plugin; 58 | 59 | std::vector FileSynchronize::rmtServers; 60 | 61 | std::vector FileSynchronize::cfilter; 62 | 63 | std::string FileSynchronize::file_name = ""; 64 | 65 | int FileSynchronize::time = 7200; 66 | 67 | std::string FileSynchronize::params = ""; 68 | 69 | int FileSynchronize::XmlParse(boost::shared_ptr init) 70 | { 71 | 72 | if ( !(IN_SYNC & IN_DELETE) ) m_delete = ""; 73 | XMLNode xMainNode = XMLNode::openFileHelper(init->config_file_name.c_str(), "head"); 74 | XMLNode xNode = xMainNode.getChildNode("sersync"); 75 | 76 | string temp = xNode.getChildNode("failLog").getAttribute("path"); 77 | if ( !temp.empty() ) file_name = temp; 78 | 79 | temp = xNode.getChildNode("failLog").getAttribute("timeToExecute"); 80 | if ( !temp.empty() ) time = atoi(temp.c_str()) * 60; 81 | 82 | //parse crontab tag 83 | temp = xNode.getChildNode("crontab").getAttribute("start"); 84 | if ( temp == "true" ) 85 | { 86 | cout << "Start the crontab " << "\t"; 87 | m_crontab = atoi(xNode.getChildNode("crontab").getAttribute("schedule")); 88 | if ( m_crontab > 0 ) 89 | { 90 | cout << "Every " << m_crontab << " minutes rsync all the files to the remote servers entirely" << endl; 91 | } else 92 | { 93 | m_crontab = 600; 94 | } 95 | 96 | temp = xNode.getChildNode("crontab").getChildNode("crontabfilter").getAttribute("start"); 97 | if ( temp == "true" ) 98 | { 99 | cout << "开启crontab过滤功能:\t" << "作整体同步的时候会对如下文件进行过滤" << endl; 100 | int num = xNode.getChildNode("crontab").getChildNode("crontabfilter").nChildNode("exclude"); 101 | for ( int i = 0; i < num; i++ ) 102 | { 103 | string t = xNode.getChildNode("crontab").getChildNode("crontabfilter").getChildNode(i).getAttribute("expression"); 104 | cout << t << endl; 105 | cfilter.push_back(t); 106 | } 107 | } 108 | 109 | if ( (!Initialize::filter.empty()) && cfilter.empty() ) 110 | { 111 | cout << "************************WARNING***********************************" << endl; 112 | cout << "您设置了文件过滤器但没有设置crontab过滤器,所以crontab会将您已经过滤的" << endl; 113 | cout << "文件整体同步到目标主机,为了达到过滤效果,请根据您设置的文件过滤器设置" << endl; 114 | cout << "crontab过滤器,注意两者正则语法可能不同,请您参考默认配置文件内容。" << endl; 115 | cout << "如果您不设置crontab过滤器,crontab将会关闭" << endl; 116 | m_crontab = -1; 117 | } 118 | } 119 | 120 | //parse rsync label 121 | temp = xNode.getChildNode("rsync").getChildNode("auth").getAttribute("start"); 122 | if ( temp == "true" ) 123 | { 124 | cout << "use rsync password-file :" << endl; 125 | m_users = xNode.getChildNode("rsync").getChildNode("auth").getAttribute("users"); 126 | m_password = xNode.getChildNode("rsync").getChildNode("auth").getAttribute("passwordfile"); 127 | cout << "user is\t" << m_users << endl; 128 | cout << "passwordfile is \t" << m_password << endl; 129 | if ( !m_password.empty() ) m_password = " --password-file=" + m_password; 130 | if ( !m_users.empty() ) m_users += "@"; 131 | } 132 | 133 | temp = xNode.getChildNode("rsync").getChildNode("ssh").getAttribute("start"); 134 | if ( temp == "true" ) ssh = " -e ssh "; 135 | 136 | 137 | temp = xNode.getChildNode("rsync").getChildNode("userDefinedPort").getAttribute("start"); 138 | if ( temp == "true" ) 139 | { 140 | string p = xNode.getChildNode("rsync").getChildNode("userDefinedPort").getAttribute("port"); 141 | port = " --port=" + p + " "; 142 | } 143 | 144 | temp = xNode.getChildNode("rsync").getChildNode("timeout").getAttribute("start"); 145 | if ( temp == "true" ) 146 | { 147 | string p = xNode.getChildNode("rsync").getChildNode("timeout").getAttribute("time"); 148 | timeout = " --timeout=" + p + " "; 149 | } 150 | 151 | temp = xNode.getChildNode("rsync").getChildNode("commonParams").getAttribute("params"); 152 | if ( !temp.empty() ) params = temp; 153 | 154 | temp = xNode.getChildNode("plugin").getAttribute("start"); 155 | if ( temp == "true" ) 156 | { 157 | cout << "after each synchronize run the plugin " << "\t"; 158 | m_plugin = xNode.getChildNode("plugin").getAttribute("name"); 159 | cout << "plugin name is: " << m_plugin << endl; 160 | } 161 | 162 | XMLNode lNode = xNode.getChildNode("localpath", 0); 163 | int wtchnum = xNode.nChildNode("localpath"); //get the numbers of the localpath tag 164 | if ( wtchnum == 0 ) 165 | { 166 | perror("ERROR,You must specify the watch localpath\n"); 167 | exit(1); 168 | } 169 | watch = xNode.getChildNode("localpath").getAttribute("watch"); 170 | watch = Initialize::SplitLastSlash(watch); 171 | 172 | /*open debug*/ 173 | if ( init->debug == 1 ) debug = 1; 174 | if ( init->deleteFlag == true ) m_delete = "--delete"; 175 | 176 | int remotenum = lNode.nChildNode("remote"); 177 | if ( remotenum == 0 ) 178 | { 179 | perror("error there are no remote servers in your config xml"); 180 | exit(1); 181 | } 182 | for ( int i = 0; i < remotenum; i++ ) 183 | { 184 | string temp_ip = lNode.getChildNode("remote", i).getAttribute("ip"); 185 | string temp_module = lNode.getChildNode("remote", i).getAttribute("name"); 186 | if ( temp_ip == "" || temp_module == "" ) 187 | { 188 | perror("error remote servers ip and moudle empty see config xml"); 189 | exit(1); 190 | } 191 | rmtServers.push_back(ptrRmtServer(new RemoteServer(temp_ip, temp_module))); 192 | } 193 | 194 | if ( rmtServers.size() ) 195 | { 196 | cout << "config xml parse success" << endl; 197 | cout << "please set /etc/rsyncd.conf max connections=0 Manually" << endl; 198 | cout << "sersync working thread " << init->sync_num + 2 << " = " << "1(primary thread) + 1(fail retry thread) + " << init->sync_num \ 199 | << "(daemon sub threads) " << endl; 200 | cout << "Max threads numbers is: " << 2 + init->sync_num + (rmtServers.size()) * init->sync_num << " = " << 2 + init->sync_num << "(Thread pool nums) + "\ 201 | << (rmtServers.size())*(init->sync_num) << "(Sub threads)" << endl; 202 | cout << "please according your cpu ,use -n param to adjust the cpu rate" << endl; 203 | return 1; 204 | } else 205 | { 206 | 207 | return 0; //fail 208 | } 209 | } 210 | 211 | FileSynchronize::FileSynchronize(ptrInitialize init, ptrQFilter qf, ptrQRetry qr) 212 | { 213 | m_init = init; 214 | m_qf = qf; 215 | m_qr = qr; // 216 | XmlParse(init); //parse xml script 217 | qr->SetRetryInfo(file_name, time); //设置失败记录脚本路径和执行时间间隔 218 | if ( init->exec_flag & RSYNC_ONCE ) 219 | { 220 | FileSynchronize::RsyncOnce(); 221 | } else 222 | { 223 | FileSynchronize::firstflag++; 224 | } 225 | 226 | ptrInterface itf; 227 | if ( !m_plugin.empty() ) 228 | { 229 | if ( m_plugin == "refreshCDN" ) 230 | { 231 | ptrInterface temp(new CdnRefreshInterface()); 232 | itf = temp; 233 | } 234 | if ( m_plugin == "socket" ) 235 | { 236 | ptrInterface temp(new SocketInterface()); 237 | itf = temp; 238 | } 239 | if ( m_plugin == "command" ) 240 | { 241 | ptrInterface temp(new CommandInterface()); 242 | itf = temp; 243 | } 244 | //if (m_plugin == "http") 245 | //{ 246 | //ptrInterface temp( new HttpInterface( ) ); 247 | //itf = temp; 248 | //} 249 | if ( itf.use_count() != 0 ) 250 | { 251 | itf->XmlParse(init->config_file_name); 252 | watch = itf->m_watch; 253 | } else 254 | { 255 | cout << "sersync plugin error,please confirm the config xml" << endl; 256 | exit(1); 257 | } 258 | 259 | } 260 | boost::thread_group syncThreads; 261 | //create threads 262 | for ( int i = 1; i <= init->sync_num; i++ ) 263 | { 264 | 265 | syncThreads.create_thread(boost::bind(&ServerSyncThread, qf, qr, itf)); 266 | } 267 | //fail retry thread create 268 | boost::thread retryThread(boost::bind(&QueueRetryThread, qr)); 269 | } 270 | 271 | void* FileSynchronize::ServerSyncThread(ptrQFilter qf, ptrQRetry qr, ptrInterface itf) 272 | { 273 | while (1) 274 | { 275 | boost::mutex::scoped_lock nlock(worklock); 276 | sleep_group++; 277 | while (qf->empty()) 278 | { 279 | work_cond.wait(nlock); 280 | } 281 | sleep_group--; 282 | Event event = qf->pop(); 283 | nlock.unlock(); 284 | boost::thread_group rsync_group; 285 | 286 | for ( int offset = 0; offset < rmtServers.size(); offset++ ) 287 | { 288 | rsync_group.create_thread(boost::bind(&RsyncThread, qr, event, rmtServers[offset]->ip, rmtServers[offset]->module, watch)); 289 | } 290 | rsync_group.join_all(); 291 | if ( itf.use_count() != 0 ) 292 | { 293 | itf->Execute(event); 294 | } 295 | 296 | } 297 | }//end ServerSyncThread 298 | 299 | void* FileSynchronize::RsyncThread(ptrQRetry qr, Event event, string ip, string module, string watch) 300 | { 301 | string eventpath = event->path; 302 | 303 | int operation = event->operation; 304 | int dir = event->dir; 305 | string path; 306 | string command = "cd " + watch + " && rsync " + params + " -R " + ssh + port + timeout; 307 | if ( operation == 1 ) // create or modify a file on the remote server 308 | { 309 | path = "." + eventpath.substr(watch.length(), (eventpath.length() - watch.length())); 310 | command += StrEscaped(path) + " "; 311 | 312 | } else // delete a file on the remote server 313 | { 314 | path = eventpath.substr(watch.length() + 1, (eventpath.length() - watch.length())); 315 | path = DelPathCombine(path, dir); 316 | command += m_delete + " ./ " + path + " "; 317 | } 318 | command += m_users + ip; 319 | if ( ssh.empty() ) 320 | { 321 | command += "::"; 322 | } else 323 | { 324 | command += ":"; 325 | } 326 | command += module + m_password; 327 | if ( !debug ) command += " >/dev/null 2>&1 "; 328 | 329 | //debug info for user 330 | if ( debug ) cout << command << endl; 331 | 332 | int res = system(command.c_str()); 333 | if ( debug_level & THREAD_DEBUG ) 334 | { 335 | cout << "Rsync command:" << command << endl; 336 | cout << "res: " << res << endl; 337 | } 338 | 339 | /*5888 is the error no permitted to set times but the file has been rsynced*/ 340 | if ( res && res != 5888 ) qr->push(command); 341 | 342 | } //end RsyncThread 343 | 344 | void FileSynchronize::ThreadAwaken() 345 | { 346 | while (!m_qf->empty()) 347 | { 348 | boost::mutex::scoped_lock nlock(worklock); 349 | if ( sleep_group > 0 ) 350 | { 351 | work_cond.notify_one(); 352 | } 353 | nlock.unlock(); 354 | } 355 | } 356 | 357 | void* FileSynchronize::QueueRetryThread(ptrQRetry qr) 358 | { 359 | boost::system_time time_start = boost::get_system_time(); 360 | boost::system_time crontabStart = boost::get_system_time(); 361 | while (1) 362 | { 363 | boost::system_time timeout = boost::get_system_time() + boost::posix_time::minutes(1); 364 | string command; 365 | bool has_command = qr->time_wait_pop(command, timeout); 366 | if ( !has_command ) 367 | { 368 | boost::system_time time_cur = boost::get_system_time(); 369 | boost::posix_time::time_duration offset = time_cur - time_start; 370 | if ( offset.total_seconds() >= qr->retryInterval ) 371 | { 372 | ExecuteScript(qr->fileName); 373 | time_start = boost::get_system_time(); 374 | } 375 | if ( m_crontab > 0 ) 376 | { 377 | time_cur = boost::get_system_time(); 378 | offset = time_cur - crontabStart; 379 | if ( offset.total_seconds() >= (m_crontab * 60) ) 380 | { 381 | RsyncOnce(); 382 | crontabStart = boost::get_system_time(); 383 | } 384 | } 385 | } else 386 | { 387 | int res = system(command.c_str()); 388 | if ( res != 0 && res != 5888 )// 5888 is the error no permitted to set times but the file has been rsynced 389 | { 390 | char temp[64]; 391 | sprintf(temp, "%d", res); 392 | string s(temp); 393 | command = "#errno " + s + "\n" + command + "\n"; 394 | qr->ErrorLog(command); // only one reader so safe 395 | } 396 | } 397 | 398 | }//end while 399 | } 400 | 401 | void FileSynchronize::ExecuteScript(string file_name) 402 | { 403 | string command = "/bin/sh " + file_name; 404 | if ( debug ) 405 | { 406 | cout << "execute script: " << endl << command << endl; 407 | } 408 | system(command.c_str()); 409 | ofstream out; 410 | out.open(file_name.c_str(), ofstream::out); //after execute clear the rsync_fail_log.sh 411 | out.close(); 412 | command = "chmod 777 " + file_name; 413 | system(command.c_str()); 414 | } 415 | 416 | /** 417 | *@param str 同步相对路径前面没有./,如 t1/t2/t3.php 418 | * @return 返回符合rsync 规则的路径 --include=t1/ --include=t1/t2 --include=t1/t2/t3.php --exclude=* 419 | */ 420 | std::string FileSynchronize::DelPathCombine(std::string path, bool dir) 421 | { 422 | string temp = " "; 423 | int pos = 0; 424 | while ((pos = path.find("/", pos)) != string::npos) 425 | { 426 | temp += " --include=" + StrEscaped(path.substr(0, pos + 1)); 427 | pos++; 428 | } 429 | temp += " --include=" + StrEscaped(path); 430 | 431 | if ( dir == true ) temp += " --include=" + StrEscaped(path + "/***"); 432 | temp += " --exclude=* "; 433 | 434 | return temp; 435 | } 436 | 437 | //======================================================================= 438 | //函数名: StrEscaped 439 | //作者: zy(zhoukunta@qq.com) 440 | //日期: 2009-04-01 441 | //功能: 将路径中的字符特殊字符转义 442 | //输入参数: path(string) 要转义的路径名 443 | //返回值: string 转义后的字符串 444 | //修改记录: 445 | //======================================================================= 446 | 447 | string FileSynchronize::StrEscaped(std::string path) 448 | { 449 | string temp = ""; 450 | for ( int i = 0; i < path.length(); i++ ) 451 | { 452 | if ( path[i] == '$' ) 453 | { 454 | temp += "\\$"; 455 | continue; 456 | } 457 | temp += path[i]; 458 | } 459 | return "\"" + temp + "\""; 460 | } 461 | 462 | 463 | 464 | //======================================================================= 465 | //name: RsyncOnce() 466 | //author: zy(zhoukunta@qq.com) 467 | //date: 2009-04-02 468 | //function: rsync all the local files to the remote servers 469 | //param: void 470 | //return: void 471 | //modify: if debug start rsync command without &1 472 | //======================================================================= 473 | int FileSynchronize::firstflag = 0; 474 | 475 | bool FileSynchronize::RsyncOnce() 476 | { 477 | if ( 0 == firstflag ) 478 | { 479 | if ( !Initialize::filter.empty() ) 480 | { 481 | cout << "************************attention***********************************" << endl; 482 | cout << "you set the filter so the \"-r \" can not work" << endl; 483 | firstflag++; 484 | return false; 485 | } 486 | cout << "------------------------------------------" << endl; 487 | cout << "rsync the directory recursivly to the remote servers once" << endl; 488 | cout << "working please wait..." << endl; 489 | } 490 | 491 | for ( int i = 0; i < rmtServers.size(); i++ ) 492 | { 493 | string command = "cd " + watch + " && rsync " + params + " -R " + m_delete + " ./ " + ssh + port + timeout; 494 | command += m_users + rmtServers[i]->ip; 495 | if ( ssh.empty() ) 496 | { 497 | command += "::"; 498 | } else 499 | { 500 | command += ":"; 501 | } 502 | command += rmtServers[i]->module + m_password; 503 | command += AddExclude(); 504 | if ( !debug ) command += " >/dev/null 2>&1 "; 505 | if ( firstflag == 0 ) 506 | { 507 | cout << "execute command: " << command << endl; 508 | firstflag++; 509 | } 510 | if ( debug ) cout << "crontab command:" << command << endl; 511 | system(command.c_str()); 512 | } 513 | } 514 | 515 | //======================================================================= 516 | //函数名: AddExclude 517 | //作者: zy(zhoukunta@qq.com) 518 | //日期: 2009-04-02 519 | //功能: 加入需要过滤的文件 520 | //输入参数: 无 521 | //返回值: string 拼好的需要过滤的文件 522 | //修改记录: 523 | //======================================================================= 524 | 525 | string FileSynchronize::AddExclude() 526 | { 527 | string command; 528 | for ( int i = 0; i < cfilter.size(); i++ ) 529 | { 530 | command += " --exclude=" + StrEscaped(cfilter[i]); 531 | } 532 | return command; 533 | } 534 | -------------------------------------------------------------------------------- /FileSynchronize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ServersFileSysc.h 3 | * Author: root 4 | * 5 | * Created on 2009年12月21日, 下午1:29 6 | */ 7 | #include 8 | #include"Initialize.h" 9 | #include"Interface.h" 10 | #include"main.h" 11 | #include 12 | #ifndef _SERVERSFILESYSC_H 13 | #define _SERVERSFILESYSC_H 14 | 15 | class FileSynchronize { 16 | public: 17 | static std::string m_plugin; 18 | ptrInitialize m_init; 19 | ptrQFilter m_qf; 20 | ptrQRetry m_qr; 21 | static std::string watch; 22 | static std::vector rmtServers; 23 | static int m_crontab; 24 | static int sleep_group; //用于统计当前处于sleep状态的线程数量 25 | static boost::condition_variable work_cond; //工作条件对象 26 | static boost::mutex worklock; //用于统计工作线程数量的锁定 27 | static std::string m_users; 28 | static std::string m_password; 29 | static std::string m_delete; 30 | static vector cfilter; 31 | static int firstflag; 32 | static std::string ssh; 33 | static std::string port; 34 | static std::string file_name; 35 | static int time; 36 | static bool debug; 37 | static string timeout; 38 | static string params; 39 | public: 40 | FileSynchronize(ptrInitialize init, ptrQFilter qf, ptrQRetry qr); 41 | static int XmlParse(ptrInitialize init); 42 | void ThreadAwaken(); 43 | static bool RsyncOnce(); 44 | protected: 45 | static std::string StrEscaped(std::string);//escaped the '$' in the path 46 | static std::string AddExclude(); 47 | static void ExecuteScript(std::string file_name); 48 | static void* ServerSyncThread(ptrQFilter qf, ptrQRetry qr, ptrInterface itf); 49 | static void* RsyncThread(ptrQRetry qr, Event event, std::string ip, std::string module, std::string watch); 50 | static void* QueueRetryThread(ptrQRetry qr); 51 | static std::string DelPathCombine(std::string str, bool dir); 52 | 53 | 54 | }; 55 | 56 | #endif /* _SERVERSFILESYSC_H */ 57 | 58 | -------------------------------------------------------------------------------- /HttpInterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "QueueFilter.h" 4 | #include "Initialize.h" 5 | #include "xmlParser.h" 6 | #include "Inotify.h" 7 | #include"HttpInterface.h" 8 | 9 | HttpInterface::HttpInterface() : m_url("http://localhost") 10 | { 11 | 12 | } 13 | 14 | void HttpInterface::XmlParse(std::string config_file_name) 15 | { 16 | XMLNode xMainNode = XMLNode::openFileHelper( config_file_name.c_str( ), "head" ); 17 | int num = xMainNode.nChildNode( "plugin" ); 18 | if (num == 0) 19 | { 20 | perror( "错误,配置文件中没有插件标签\n" ); 21 | exit( 1 ); 22 | } 23 | for (int i = 0; i < num; i++) 24 | { 25 | string name = xMainNode.getChildNode( "plugin", i ).getAttribute( "name" ); 26 | if (name == "http") 27 | { 28 | XMLNode xNode = xMainNode.getChildNode( "plugin", i ); 29 | m_watch = xNode.getChildNode( "localpath" ).getAttribute( "watch" ); 30 | m_url = xNode.getChildNode( "localpath" ).getChildNode( "deshost" ).getAttribute( "url" ); 31 | m_port = atoi( xNode.getChildNode( "localpath" ).getChildNode( "deshost" ).getAttribute( "port" ) ); 32 | cout << "监控路径: " << m_watch << " URL: " << m_url << " 端口号: " << m_port << endl; 33 | break; 34 | } 35 | } 36 | } 37 | 38 | int HttpInterface::Execute(Event e) 39 | { 40 | CURL *curl; 41 | CURLM *multi_handle; 42 | int still_running; 43 | int times = 0; //try times if select false 44 | int TRY_TIMES = 50; 45 | char strMask[10]; 46 | sprintf( strMask, "%d", e->mask ); 47 | string sm = strMask; 48 | string sendBuffer = "watchroot=" + m_watch; 49 | if (e->dir) 50 | { 51 | sendBuffer += "&isdir=1"; 52 | } else 53 | { 54 | sendBuffer += "&isdir=0"; 55 | } 56 | sendBuffer += "&inotifyPath=" + e->path; 57 | sendBuffer += "&mask=" + sm; 58 | 59 | curl = curl_easy_init( ); 60 | multi_handle = curl_multi_init( ); 61 | if (curl && multi_handle) 62 | { 63 | /* what URL that receives this POST */ 64 | curl_easy_setopt( curl, CURLOPT_URL, m_url.c_str( ) ); 65 | curl_easy_setopt( curl, CURLOPT_HTTPPOST, 1 ); 66 | curl_easy_setopt( curl, CURLOPT_POSTFIELDS, sendBuffer.c_str( ) ); 67 | curl_multi_add_handle( multi_handle, curl ); 68 | while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform( multi_handle, &still_running )); 69 | while (still_running && times < TRY_TIMES) 70 | { 71 | int rc; //select() return code 72 | int maxfd; 73 | fd_set fdread; 74 | fd_set fdwrite; 75 | fd_set fdexcep; 76 | FD_ZERO( &fdread ); 77 | FD_ZERO( &fdwrite ); 78 | FD_ZERO( &fdexcep ); 79 | //get file descriptors from the transfers 80 | curl_multi_fdset( multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd ); 81 | rc = select( maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout ); 82 | switch (rc) 83 | { 84 | case -1://select error 85 | break; 86 | case 0: 87 | default: // timeout 88 | while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform( multi_handle, &still_running )); 89 | break; 90 | } 91 | times++; 92 | }//end while 93 | curl_multi_remove_handle( multi_handle, curl ); 94 | curl_easy_cleanup( curl ); 95 | curl_multi_cleanup( multi_handle ); //always cleanup 96 | if (times >= TRY_TIMES) 97 | { 98 | return 1; 99 | } 100 | return 0; 101 | }//end if 102 | return 1; 103 | } 104 | -------------------------------------------------------------------------------- /HttpInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: HttpInterface.h 3 | * Author: zy 4 | * 5 | * Created on 2010年1月23日, 下午11:57 6 | */ 7 | 8 | #ifndef _HTTPINTERFACE_H 9 | #define _HTTPINTERFACE_H 10 | #include"Interface.h" 11 | 12 | class HttpInterface : public Interface { 13 | public: 14 | std::string m_url; 15 | public: 16 | HttpInterface(); 17 | virtual void XmlParse(std::string config_file_name); 18 | virtual int Execute(Event e); 19 | }; 20 | 21 | #endif /* _HTTPINTERFACE_H */ 22 | 23 | -------------------------------------------------------------------------------- /Initialize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Initialize.h" 5 | #include "xmlParser.h" 6 | #include"main.h" 7 | using namespace std; 8 | 9 | extern int optind; 10 | extern int optopt; 11 | extern char *optarg; 12 | extern int IN_SYNC; 13 | #define DEFAULT_THREAD_NUM 10 14 | int getopt(int argc, char * const *argv, const char *optstring); 15 | 16 | std::vector Initialize::filter; 17 | bool Initialize::debug = false; 18 | bool Initialize::createFile = false; 19 | bool Initialize::createFolder = false; 20 | bool Initialize::xfs = false; 21 | 22 | Initialize::Initialize(int argc, char *argv[]) : exec_flag(0), config_file_name("confxml.xml"), sync_num(0), module("sersync") 23 | { 24 | ConfigInotify(); //modify system param 25 | CammandParse(argc, argv); //parse command param 26 | XmlParse(); 27 | } 28 | 29 | int Initialize::XmlParse() 30 | { 31 | cout << "parse xml config file" << endl; 32 | XMLNode xMainNode = XMLNode::openFileHelper(config_file_name.c_str(), "head"); 33 | XMLNode xNode = xMainNode.getChildNode("host"); 34 | 35 | hostip = xNode.getAttribute("hostip"); 36 | cout << "host ip : " << hostip << "\t"; 37 | 38 | port = atoi(xNode.getAttribute("port")); 39 | cout << "host port: " << port << endl; 40 | 41 | xNode = xMainNode.getChildNode("filter"); 42 | string temp = xNode.getAttribute("start"); 43 | if ( temp == "true" ) 44 | { 45 | cout << "now the filter work ,if you set the crontab,you have to set crontab filter " << endl; 46 | int num = xNode.nChildNode("exclude"); 47 | for ( int i = 0; i < num; i++ ) 48 | { 49 | string t = xNode.getChildNode(i).getAttribute("expression"); 50 | filter.push_back(t); 51 | } 52 | } 53 | 54 | xNode = xMainNode.getChildNode("inotify").getChildNode("delete"); 55 | temp = xNode.getAttribute("start"); 56 | if ( temp == "true" ) 57 | { 58 | IN_SYNC |= IN_DELETE; 59 | deleteFlag = true; 60 | } else 61 | { 62 | cout << "will ignore the inotify delete event" << endl; 63 | } 64 | 65 | temp = xMainNode.getChildNode("inotify").getChildNode("createFile").getAttribute("start"); 66 | if ( temp == "true" ) 67 | { 68 | IN_SYNC |= IN_CREATE; 69 | createFile = true; 70 | cout << "will ignore the inotify createFile event " << endl; 71 | } 72 | 73 | temp = xMainNode.getChildNode("inotify").getChildNode("createFolder").getAttribute("start"); 74 | if ( temp == "true" ) 75 | { 76 | IN_SYNC |= IN_CREATE; 77 | createFolder = true; 78 | } 79 | 80 | temp = xMainNode.getChildNode("inotify").getChildNode("closeWrite").getAttribute("start"); 81 | if ( temp == "true" ) IN_SYNC |= IN_CLOSE_WRITE; 82 | 83 | temp = xMainNode.getChildNode("inotify").getChildNode("moveFrom").getAttribute("start"); 84 | if ( temp == "true" ) IN_SYNC |= IN_MOVED_FROM; 85 | 86 | temp = xMainNode.getChildNode("inotify").getChildNode("moveTo").getAttribute("start"); 87 | if ( temp == "true" ) IN_SYNC |= IN_MOVED_TO; 88 | 89 | temp = xMainNode.getChildNode("inotify").getChildNode("attrib").getAttribute("start"); 90 | if ( temp == "true" ) IN_SYNC |= IN_ATTRIB; 91 | 92 | temp = xMainNode.getChildNode("inotify").getChildNode("modify").getAttribute("start"); 93 | if ( temp == "true" ) IN_SYNC |= IN_MODIFY; 94 | 95 | 96 | xNode = xMainNode.getChildNode("debug"); 97 | temp = xNode.getAttribute("start"); 98 | if ( temp == "true" ) 99 | { 100 | debug = 1; 101 | cout << "Open debug, you will see debug infomation " << endl; 102 | } 103 | 104 | xNode = xMainNode.getChildNode("fileSystem"); 105 | temp = xNode.getAttribute("xfs"); 106 | if ( temp == "true" ) 107 | { 108 | xfs = true; 109 | cout << "WARNING XFS FILE SYSTEM WORK" << endl; 110 | } 111 | 112 | } 113 | 114 | int Initialize::CammandParse(int argc, char* argv[]) 115 | { 116 | optarg = NULL; 117 | string temp; 118 | int ch; 119 | cout << "parse the command param" << endl; 120 | while ((ch = getopt(argc, argv, "hdro:n:m:")) != -1) 121 | { 122 | 123 | switch (ch) 124 | { 125 | case 'd': 126 | printf("option: -d \t"); 127 | printf("run as a daemon\n"); 128 | exec_flag |= OPEN_DAEMON; 129 | break; 130 | case 'r': 131 | printf("option: -r \t"); 132 | printf("rsync all the local files to the remote servers before the sersync work\n"); 133 | exec_flag |= RSYNC_ONCE; 134 | break; 135 | case 'o': 136 | printf("option: -o \t"); 137 | printf("config xml name: %s\n", optarg); 138 | config_file_name = optarg; 139 | if ( config_file_name.empty() ) 140 | { 141 | cout << "ERROR config file name empty" << endl; 142 | exit(1); 143 | } 144 | exec_flag |= OTHER_CONFNAME; 145 | break; 146 | case 'm': 147 | printf("option: -m \t"); 148 | printf("working plugin name: %s\n", optarg); 149 | module = "other"; 150 | temp = optarg; 151 | if ( temp == "refreshCDN" ) 152 | { 153 | exec_flag |= REFRESHCDN_MODULE; 154 | } else if ( temp == "socket" ) 155 | { 156 | exec_flag |= SOCKET_MODULE; 157 | } else if ( temp == "http" ) 158 | { 159 | exec_flag |= HTTP_MODULE; 160 | } else if ( temp == "command" ) 161 | { 162 | exec_flag |= COMMAND_MODULE; 163 | } 164 | break; 165 | case 'h': 166 | cout << "_______________________________________________________" << endl; 167 | cout << "参数-d:启用守护进程模式" << endl; 168 | cout << "参数-r:在监控前,将监控目录与远程主机用rsync命令推送一遍" << endl; 169 | cout << "c参数-n: 指定开启守护线程的数量,默认为10个" << endl; 170 | cout << "参数-o:指定配置文件,默认使用confxml.xml文件" << endl; 171 | cout << "参数-m:单独启用其他模块,使用 -m refreshCDN 开启刷新CDN模块" << endl; 172 | cout << "参数-m:单独启用其他模块,使用 -m socket 开启socket模块" << endl; 173 | cout << "参数-m:单独启用其他模块,使用 -m http 开启http模块" << endl; 174 | cout << "不加-m参数,则默认执行同步程序" << endl; 175 | cout << "________________________________________________________________" << endl; 176 | exit(0); 177 | case 'n': 178 | printf("option: -n \t"); 179 | printf("thread num is: %s\n", optarg); 180 | sync_num = atoi(optarg); 181 | exec_flag |= SYN_NUM; 182 | break; 183 | case '?': 184 | printf("命令行参数错误: 没有该选项-%c\n", (char) optopt); 185 | exit(1); 186 | } 187 | }//end while 188 | if ( sync_num == 0 ) 189 | { 190 | sync_num = DEFAULT_THREAD_NUM; 191 | cout << "daemon thread num: " << DEFAULT_THREAD_NUM << endl; 192 | } 193 | } 194 | 195 | void Initialize::ConfigInotify() 196 | { 197 | cout << "set the system param" << endl; 198 | cout << "execute:echo 50000000 > /proc/sys/fs/inotify/max_user_watches" << endl; 199 | system("echo 50000000 > /proc/sys/fs/inotify/max_user_watches"); 200 | cout << "execute:echo 327679 > /proc/sys/fs/inotify/max_queued_events" << endl; 201 | system("echo 327679 > /proc/sys/fs/inotify/max_queued_events"); 202 | 203 | } 204 | 205 | string Initialize::SplitLastSlash(string str) 206 | { 207 | if ( str.empty() ) 208 | { 209 | perror("the path your watch don't exists"); 210 | exit(1); 211 | } 212 | if ( str.find_last_of("/") == (str.length() - 1) ) 213 | { 214 | str = str.substr(0, str.length() - 1); 215 | } 216 | return str; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /Initialize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Initialize.h 3 | * Author: root 4 | * 5 | * Created on 2010年1月10日, 下午7:55 6 | */ 7 | 8 | #ifndef _INITIALIZE_H 9 | #define _INITIALIZE_H 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #define OPEN_DAEMON 0x01 16 | #define RSYNC_ONCE 0x01<<1 17 | #define SYN_NUM 0x01<<2 18 | #define OTHER_CONFNAME 0x01<<3 19 | #define EXECUTE_SCRIPT 0x01<<5 20 | #define SERSYNC_MODULE 0x01<<6 21 | #define REFRESHCDN_MODULE 0x01<<7 22 | #define HTTP_MODULE 0x01<<8 23 | #define SOCKET_MODULE 0x01<<9 24 | #define COMMAND_MODULE 0x01<<10 25 | 26 | struct RemoteServer { 27 | std::string ip; 28 | std::string module; 29 | 30 | inline RemoteServer(std::string ip = "", std::string module = "") : ip(ip), module(module) { 31 | } 32 | }; 33 | typedef boost::shared_ptr ptrRmtServer; 34 | 35 | class Initialize { 36 | public: 37 | int exec_flag; 38 | int sync_num; 39 | std::string module; 40 | std::string config_file_name; 41 | std::string hostip; 42 | static std::vector filter; 43 | int port; 44 | static bool debug; 45 | bool deleteFlag; 46 | static bool createFile; 47 | static bool createFolder; 48 | static bool xfs; 49 | public: 50 | Initialize(int argc, char *argv[]); 51 | void ConfigInotify(); 52 | int XmlParse(); 53 | static std::string SplitLastSlash(std::string str); 54 | protected: 55 | int CammandParse(int argc, char *argv[]); 56 | 57 | }; 58 | typedef boost::shared_ptr ptrInitialize; 59 | #endif /* _INITIALIZE_H */ 60 | 61 | -------------------------------------------------------------------------------- /Inotify.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Initialize.h" 12 | #include "Inotify.h" 13 | #include"main.h" 14 | 15 | //最大缓冲数量 16 | #define MAX_BUF_SIZE 5120 17 | bool printEvent(inotify_event *e); 18 | int flag; //store inotify resource descriptor status; 19 | struct timeval tv; 20 | fd_set rfds; 21 | extern int debug_level; 22 | extern int IN_SYNC; 23 | using namespace std; 24 | std::vector Inotify::pattern; 25 | int Inotify::m_first_add_watch = 1; 26 | Inotify::Inotify(string rootPath) 27 | { 28 | //初始化正则表达式对象 29 | Inotify::InitReguar(); 30 | m_fd = inotify_init(); 31 | m_watch = rootPath; //do not have the '/' tail; 32 | AddWatch(rootPath); 33 | m_first_add_watch = 0; 34 | flag = fcntl(m_fd, F_GETFL, 0); 35 | tv.tv_sec = 0; 36 | tv.tv_usec = 500000; 37 | FD_ZERO(&rfds); 38 | FD_SET(m_fd, &rfds); 39 | } 40 | 41 | //======================================================================= 42 | //函数名: InitReguar 43 | //作者: zy(zhoukunta@qq.com) 44 | //日期: 2009-04-05 45 | //功能: 初始化正则表达式对象 46 | //输入参数:无 47 | //返回值: 无 48 | //修改记录: 49 | //======================================================================= 50 | 51 | void* Inotify::InitReguar() 52 | { 53 | for ( int i = 0; i < Initialize::filter.size(); i++ ) 54 | { 55 | try 56 | { 57 | ptrRegex tmp(new boost::regex((Initialize::filter[i]))); 58 | pattern.push_back(tmp); 59 | } catch (boost::regex_error& e) 60 | { 61 | cout << endl << "filter 中" << Initialize::filter[i] << "regular expression error,filter fail" << endl; 62 | cout << "error content:\t" << e.what() << endl; 63 | continue; 64 | } 65 | } 66 | } 67 | 68 | int Inotify::inotify_read_times = 3; 69 | 70 | unsigned int Inotify::GetEvents(ptrQFilter q) 71 | { 72 | int times = 1; 73 | int again = ReadFill(q); 74 | fcntl(m_fd, F_SETFL, flag | O_NONBLOCK); 75 | while (again && (times < inotify_read_times)) 76 | { 77 | FD_ZERO(&rfds); 78 | FD_SET(m_fd, &rfds); 79 | int retval = select(m_fd + 1, &rfds, NULL, NULL, &tv); 80 | if ( retval ) again = ReadFill(q); 81 | times++; 82 | } 83 | fcntl(m_fd, F_SETFL, flag); 84 | return 1; 85 | } 86 | //======================================================================= 87 | //function name: ReadFill 88 | //author: zy(zhoukunta@qq.com) 89 | //date: 2009-01-24 90 | //function: 将读到的一个或多个inotify事件填入队列 91 | //imput params:q(ptrQFilter)队列的指针对象 92 | // 93 | //return: true有事件填入 false无事件填入 94 | //======================================================================= 95 | 96 | bool Inotify::ReadFill(ptrQFilter q) 97 | { 98 | int len = 0; 99 | char buffer[MAX_BUF_SIZE]; 100 | char* offset = buffer; 101 | len = read(m_fd, buffer, MAX_BUF_SIZE); //read May lead to this programme blocking 102 | if ( len == -1 ) return false; //inotify queue empty 103 | while (offset - buffer < len) 104 | { 105 | inotify_event* event = (struct inotify_event*) offset; 106 | offset += sizeof ( struct inotify_event) +event->len; 107 | if ( Initialize::debug == true ) 108 | { 109 | cout << "inotify wd:" << event->wd << "\tname:" << event->name << "\tmask:" << event->mask << endl; 110 | } 111 | if ( FilterEvent(event) ) continue; //if tmp file continue; 112 | if ( !pattern.empty() && FilterExclude(event) ) continue; //filter match regular pattern in xml 113 | if ( debug_level & INOTIFY_DEBUG ) printEvent(event); 114 | FillEvent(event, q); 115 | } 116 | return true; 117 | } 118 | 119 | int Inotify::FillEvent(inotify_event* event, ptrQFilter que) 120 | { 121 | 122 | Event tempEvent(new InotifyEvent()); 123 | tempEvent->wd = event->wd; 124 | tempEvent->path = (m_path[ event->wd ] + "/" + event->name); //sys sometimes produce large ie.wd which we don't add in the wd list 125 | tempEvent->mask = event->mask; 126 | 127 | if ( event->mask & IN_IGNORED ) 128 | { 129 | tempEvent->operation = -1; 130 | return 0; 131 | } else if ( (event->mask & IN_MOVED_FROM) || (event->mask & IN_DELETE) ) 132 | { 133 | tempEvent->operation = 0; 134 | } else if ( 135 | (event->mask & IN_CLOSE_WRITE) || 136 | (event->mask & IN_MODIFY) || 137 | (event->mask & IN_CREATE) || 138 | (event->mask & IN_MOVED_TO) ) 139 | { 140 | tempEvent->operation = 1; 141 | } 142 | 143 | if ( event->mask & IN_ISDIR )//remove or delete directory 144 | { 145 | tempEvent->dir = true; 146 | if ( tempEvent->operation == 0 ) //delete folder 147 | { 148 | RemoveWatch(tempEvent->path); 149 | } else if ( tempEvent->operation == 1 )//create folder add watch 150 | { 151 | AddWatch(tempEvent->path); 152 | } 153 | } else 154 | { 155 | tempEvent->dir = false; 156 | } 157 | //add to rsync queue 158 | que->push(tempEvent); 159 | return 1; 160 | } 161 | 162 | 163 | //======================================================================= 164 | //function name: AddWatch 165 | //author: zy(zhoukunta@qq.com) 166 | //date: 2010-06-02 167 | //function: add the path to inotify api 168 | //input params: path(string) the path need to watch 169 | //return: bool true if success else false 170 | //======================================================================= 171 | 172 | bool Inotify::AddWatch(std::string path) 173 | { 174 | int wd = inotify_add_watch(m_fd, path.c_str(), IN_SYNC); 175 | if ( -1 == wd ) 176 | { 177 | perror("inotify_add_watch error"); 178 | return false; 179 | } 180 | 181 | if ( Initialize::debug == true ) 182 | { 183 | cout << "add watch: " << path << " return wd is: " << wd << endl; 184 | } 185 | 186 | m_path.insert(PathPair(wd, path)); 187 | DIR* pdir = NULL; 188 | struct dirent *pfile = NULL; 189 | if ( !(pdir = opendir(path.c_str())) ) return false; 190 | 191 | while ((pfile = readdir(pdir))) //read directory content and add recursively 192 | { 193 | string pre = path + "/" + pfile->d_name; 194 | bool is_dir = false; 195 | if ( Initialize::xfs == true ) 196 | { 197 | is_dir = IsXfsDir(pre); 198 | } else 199 | { 200 | is_dir = (pfile->d_type == 4); 201 | } 202 | if ( is_dir && strcmp(pfile->d_name, ".") && strcmp(pfile->d_name, "..") ) 203 | { 204 | if ( m_first_add_watch ) 205 | { 206 | int length = pre.size() - (m_watch.size() + 1); //relative directory length m_watch+'/' 207 | string tmp = pre.substr(m_watch.size() + 1, length) + "/"; 208 | boost::cmatch what; 209 | int filter = 0; 210 | for ( int i = 0; i < pattern.size(); i++ ) 211 | { 212 | if ( boost::regex_match(tmp.c_str(), what, *(pattern[i])) ) 213 | { 214 | filter = 1; 215 | break; 216 | } 217 | } 218 | if ( filter ) continue; 219 | } 220 | AddWatch(pre); 221 | } 222 | } 223 | closedir(pdir); 224 | return true; 225 | } 226 | 227 | bool Inotify::IsXfsDir(std::string path) 228 | { 229 | struct stat sb; 230 | if ( stat(path.c_str(), &sb) == -1 ) return false; 231 | return S_ISDIR(sb.st_mode); 232 | } 233 | 234 | /* 235 | *input params: string path not end up with slash /opt/tongbu/temp 236 | *function: remove the path from watching deque 237 | *return: success true else false; 238 | */ 239 | bool Inotify::RemoveWatch(std::string path) 240 | { 241 | bool finded = false; 242 | for ( PathMap::iterator i = m_path.begin(); i != m_path.end(); i++ ) 243 | { 244 | if ( i->second.substr(0, path.length()) == path ) //查找已经添加的监控 245 | { 246 | if ( (i->second[ path.length() ]) == '/' || i->second[ path.length() ] == 0 )//ignore the error /1234/123/ 1234/12/ as the same directory tree 247 | { 248 | inotify_rm_watch(m_fd, i->first); 249 | m_path.erase(i); 250 | finded = true; //找到并删除 251 | } 252 | } 253 | } 254 | if ( !finded ) return false; 255 | return true; 256 | } 257 | 258 | //======================================================================= 259 | //函数名: FilterExclude 260 | //作者: zy(zhoukunta@qq.com) 261 | //date: 2009-04-05 262 | //function: filter Exclude path and file 263 | //输入参数:offset(struct inotify_event*) 264 | // 265 | //return: 1该事件需要过滤 0该事件不需过滤 266 | //======================================================================= 267 | 268 | int Inotify::FilterExclude(struct inotify_event* offset) 269 | { 270 | int isfilter = 0; 271 | boost::cmatch what; 272 | string tmp = m_path[ offset->wd ]; 273 | string pre = ""; 274 | int length; 275 | if ( (length = (tmp.size() - m_watch.size())) > 0 ) 276 | { 277 | pre = tmp.substr(m_watch.size(), length); 278 | pre = pre + "/" + offset->name; 279 | } else 280 | { 281 | pre = offset->name; //root path 282 | } 283 | for ( int i = 0; i < pattern.size(); i++ ) 284 | { 285 | if ( boost::regex_match(pre.c_str(), what, *(pattern[i])) ) return 1; 286 | } 287 | 288 | return 0; 289 | } 290 | 291 | int Inotify::FilterEvent(struct inotify_event* offset) 292 | { 293 | int isfilter = 0; 294 | do 295 | { 296 | isfilter = (NULL == offset); 297 | if ( isfilter ) break; 298 | //filter create file Event 299 | isfilter = (Initialize::createFile==false)&&((offset->mask & IN_ISDIR) == 0)&&(offset->mask & IN_CREATE); 300 | if(isfilter) break; 301 | 302 | isfilter = (Initialize::createFolder ==false)&&((offset->mask & IN_ISDIR))&&(offset->mask & IN_CREATE); 303 | if(isfilter) break; 304 | 305 | isfilter = ((strcmp(offset->name, "4913") == 0) && ((offset->mask & IN_ISDIR) == 0)); 306 | isfilter = isfilter || (strcmp((offset->name), "") == 0); 307 | if ( isfilter ) break; 308 | 309 | int length = strlen((offset) ->name); 310 | isfilter = (((offset->name[ length - 1 ] == '~') || ((offset) ->name[ 0 ] == '.')) && (!(offset->mask & IN_ISDIR))); 311 | if ( isfilter ) break; 312 | 313 | 314 | isfilter = (0 == offset->wd || 0 == offset->mask); 315 | if ( isfilter ) break; 316 | 317 | PathMap::reverse_iterator last = m_path.rbegin(); 318 | isfilter = ((offset->wd) > (last->first)); 319 | if ( isfilter ) break; 320 | 321 | isfilter = (Initialize::createFile == false) && ((offset->mask & IN_ISDIR) == 0) && (offset->mask & IN_CREATE); 322 | if ( isfilter ) break; 323 | 324 | isfilter = (Initialize::createFolder == false) && (offset->mask & IN_ISDIR) && (offset->mask & IN_CREATE); 325 | if ( isfilter ) break; 326 | 327 | } while (0); 328 | 329 | return isfilter; 330 | } 331 | 332 | bool printEvent(inotify_event *e) 333 | { 334 | if ( debug_level & SUB_CLASS ) 335 | { 336 | cout << "name: " << e->name << "\t" << "mask: " << e->mask << "\t" << "len: " << e->len << endl; 337 | } 338 | 339 | } 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | -------------------------------------------------------------------------------- /Inotify.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "Initialize.h" 9 | #include"QueueFilter.h" 10 | 11 | #ifndef _INOTIFY_H 12 | #define _INOTIFY_H 13 | //需要监控的事件 14 | 15 | 16 | struct InotifyEvent { 17 | int wd; //watch descriptor for the function named inotify_add_watch 18 | std::string path; 19 | bool dir; // true if is dir; 20 | int mask; // event mask response to IN_SYNC 21 | int operation; // create/delete = 1/0 22 | }; 23 | 24 | typedef std::map PathMap; 25 | typedef std::pair PathPair; 26 | typedef boost::shared_ptr Event; 27 | typedef boost::shared_ptr ptrRegex; 28 | 29 | /** 30 | * Inotify 提供对指定目录的完整递归监控及对监控的管理。 31 | * 32 | */ 33 | class Inotify { 34 | protected: 35 | PathMap m_path; // source under watching 36 | int m_fd; //watch resource identifier 37 | string m_watch; 38 | static std::vector pattern; 39 | static int m_first_add_watch; 40 | static int inotify_read_times; 41 | public: 42 | Inotify(std::string rootPath); //add root path which want to watch 43 | bool AddWatch(std::string path); 44 | bool RemoveWatch(std::string path); //remove watch path 45 | unsigned int GetEvents(ptrQFilter que); 46 | protected: 47 | int FilterEvent(inotify_event* event); //filt redundant or error event 48 | int FilterExclude(inotify_event* event); // filt the exclude directory or file 49 | int FillEvent(inotify_event* event, ptrQFilter que); 50 | static void* InitReguar(); 51 | bool IsXfsDir(std::string path); 52 | private: 53 | bool ReadFill(ptrQFilter que); 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /Interface.cpp: -------------------------------------------------------------------------------- 1 | #include"Interface.h" 2 | 3 | Interface::Interface() : m_port(80) 4 | { 5 | timeout.tv_sec = 0; 6 | timeout.tv_usec = 100000; 7 | } 8 | -------------------------------------------------------------------------------- /Interface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Interface.h 3 | * Author: root 4 | * 5 | * Created on 2010年1月24日, 下午6:02 6 | */ 7 | 8 | #ifndef _INTERFACE_H 9 | #define _INTERFACE_H 10 | #include 11 | #include 12 | #include"Inotify.h" 13 | 14 | class Interface { 15 | public: 16 | int m_port; 17 | struct timeval timeout; 18 | std::string m_watch; 19 | public: 20 | Interface(); 21 | virtual void XmlParse(std::string config_file_name) = 0; 22 | virtual int Execute(Event e) = 0; 23 | }; 24 | 25 | typedef boost::shared_ptr ptrInterface; 26 | #endif /* _INTERFACE_H */ 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # There exist several targets which are by default empty and which can be 3 | # used for execution of your targets. These targets are usually executed 4 | # before and after some main targets. They are: 5 | # 6 | # .build-pre: called before 'build' target 7 | # .build-post: called after 'build' target 8 | # .clean-pre: called before 'clean' target 9 | # .clean-post: called after 'clean' target 10 | # .clobber-pre: called before 'clobber' target 11 | # .clobber-post: called after 'clobber' target 12 | # .all-pre: called before 'all' target 13 | # .all-post: called after 'all' target 14 | # .help-pre: called before 'help' target 15 | # .help-post: called after 'help' target 16 | # 17 | # Targets beginning with '.' are not intended to be called on their own. 18 | # 19 | # Main targets can be executed directly, and they are: 20 | # 21 | # build build a specific configuration 22 | # clean remove built files from a configuration 23 | # clobber remove all built files 24 | # all build all configurations 25 | # help print help mesage 26 | # 27 | # Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and 28 | # .help-impl are implemented in nbproject/makefile-impl.mk. 29 | # 30 | # Available make variables: 31 | # 32 | # CND_BASEDIR base directory for relative paths 33 | # CND_DISTDIR default top distribution directory (build artifacts) 34 | # CND_BUILDDIR default top build directory (object files, ...) 35 | # CONF name of current configuration 36 | # CND_PLATFORM_${CONF} platform name (current configuration) 37 | # CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) 38 | # CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) 39 | # CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) 40 | # CND_PACKAGE_DIR_${CONF} directory of package (current configuration) 41 | # CND_PACKAGE_NAME_${CONF} name of package (current configuration) 42 | # CND_PACKAGE_PATH_${CONF} path to package (current configuration) 43 | # 44 | # NOCDDL 45 | 46 | 47 | # Environment 48 | MKDIR=mkdir 49 | CP=cp 50 | CCADMIN=CCadmin 51 | RANLIB=ranlib 52 | 53 | 54 | # build 55 | build: .build-post 56 | 57 | .build-pre: 58 | # Add your pre 'build' code here... 59 | 60 | .build-post: .build-impl 61 | # Add your post 'build' code here... 62 | 63 | 64 | # clean 65 | clean: .clean-post 66 | 67 | .clean-pre: 68 | # Add your pre 'clean' code here... 69 | 70 | .clean-post: .clean-impl 71 | # Add your post 'clean' code here... 72 | 73 | 74 | # clobber 75 | clobber: .clobber-post 76 | 77 | .clobber-pre: 78 | # Add your pre 'clobber' code here... 79 | 80 | .clobber-post: .clobber-impl 81 | # Add your post 'clobber' code here... 82 | 83 | 84 | # all 85 | all: .all-post 86 | 87 | .all-pre: 88 | # Add your pre 'all' code here... 89 | 90 | .all-post: .all-impl 91 | # Add your post 'all' code here... 92 | 93 | 94 | # help 95 | help: .help-post 96 | 97 | .help-pre: 98 | # Add your pre 'help' code here... 99 | 100 | .help-post: .help-impl 101 | # Add your post 'help' code here... 102 | 103 | 104 | ## 这makefile的嵌套看来不需要前身声明啊, 放在最后都能查找到 105 | # include project implementation makefile 106 | include nbproject/Makefile-impl.mk 107 | 108 | # include project make variables 109 | include nbproject/Makefile-variables.mk 110 | -------------------------------------------------------------------------------- /QueueFilter.cpp: -------------------------------------------------------------------------------- 1 | #include"QueueFilter.h" 2 | #include "Inotify.h" 3 | #include"main.h" 4 | using namespace std; 5 | #define MAX_SEARCH_LENGTH 10 6 | 7 | QueueFilter::QueueFilter( ) 8 | { 9 | 10 | } 11 | 12 | Event QueueFilter::pop( ) 13 | { 14 | boost::mutex::scoped_lock lock(queue_mutex); 15 | Event event = que.back(); 16 | que.pop_back(); 17 | return event; 18 | } 19 | 20 | bool QueueFilter::empty( ) 21 | { 22 | boost::mutex::scoped_lock lock(queue_mutex); 23 | return que.empty(); 24 | } 25 | 26 | int QueueFilter::push( Event event ) 27 | { 28 | boost::mutex::scoped_lock lock(queue_mutex); 29 | if( !filter(event) ) 30 | { 31 | que.push_front(event); 32 | return 1; 33 | } 34 | return 0; 35 | } 36 | 37 | int QueueFilter::filter( Event event ) 38 | { 39 | 40 | int searchDistance = 0; 41 | if( event->dir == true && event->operation == 0 ) 42 | { 43 | std::deque::iterator i = que.begin(); 44 | string dirPath = event->path + "/"; //in case this path name is the prefix of other file name 45 | while( i < que.end() ) 46 | { 47 | string temp = ((*i)->path).substr(0, dirPath.length()); 48 | if( ((*i)->path).substr(0, dirPath.length()) == dirPath ) 49 | { 50 | if( debug_level & SUB_CLASS ) 51 | { 52 | cout << "erase now" << "\t" << (*i)->path << endl; 53 | } 54 | i = que.erase(i); 55 | continue; 56 | } 57 | i++; 58 | } 59 | } 60 | searchDistance = 0; 61 | std::deque::iterator i = que.begin(); 62 | while( i < que.end() && searchDistance <= MAX_SEARCH_LENGTH ) 63 | { 64 | if( (*i)->path == event->path ) 65 | { 66 | (*i)->operation = event->operation; 67 | break; 68 | } 69 | i++; 70 | searchDistance++; 71 | } 72 | if( i != que.end() && searchDistance < MAX_SEARCH_LENGTH ) 73 | { 74 | return 1; 75 | } 76 | return 0; 77 | } 78 | 79 | void QueueFilter::printdeque( ) 80 | { 81 | boost::mutex::scoped_lock lock(queue_mutex); 82 | int cre = 0; 83 | int del = 0; 84 | for( std::deque::iterator itrt = que.begin(); itrt < que.end(); itrt++ ) 85 | { 86 | std::cout << "queue filter" << (*itrt)->path << "\t"; 87 | cout << (*itrt)->operation << endl; 88 | if( (*itrt)->operation == 1 ) 89 | { 90 | cre++; 91 | } 92 | if( (*itrt)->operation == 0 ) 93 | { 94 | del++; 95 | } 96 | } 97 | if( que.empty() ) 98 | { 99 | cout << "empty Filter Queue" << endl; 100 | }else 101 | { 102 | cout << "Queue Filter" << "\t"; 103 | cout << "amount: " << que.size() << "\t"; 104 | cout << "create: " << cre << "\t"; 105 | cout << "delete: " << del << "\t"; 106 | std::cout << endl << endl << "\t"; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /QueueFilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: QueueFilter.h 3 | * Author: root 4 | * 5 | * Created on 2009年11月26日, 上午7:12 6 | */ 7 | #ifndef _QUEUEFILTER_H 8 | #define _QUEUEFILTER_H 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | struct InotifyEvent; 18 | typedef boost::shared_ptr Event; 19 | 20 | class QueueFilter { 21 | private: 22 | boost::mutex queue_mutex; 23 | boost::condition_variable con; 24 | std::deque que; 25 | public: 26 | QueueFilter(); 27 | int push(Event e); 28 | Event pop(); 29 | void printdeque(); 30 | bool empty(); 31 | private: 32 | int filter(Event e); 33 | }; 34 | typedef boost::shared_ptr ptrQFilter; 35 | #endif /* _QUEUEFILTER_H */ 36 | 37 | -------------------------------------------------------------------------------- /QueueRetry.cpp: -------------------------------------------------------------------------------- 1 | #include"QueueRetry.h" 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | QueueRetry::QueueRetry() : fileName("rsync_fail_log.sh"), retryInterval(7200) 7 | { 8 | 9 | } 10 | 11 | void QueueRetry::SetRetryInfo(string file_name, int time) 12 | { 13 | fileName = file_name; 14 | retryInterval = time; 15 | ofstream out; 16 | out.open(fileName.c_str(), ofstream::app); 17 | out.close(); 18 | string command = "chmod 777 " + fileName; 19 | system(command.c_str()); 20 | } 21 | 22 | bool QueueRetry::empty() 23 | { 24 | boost::mutex::scoped_lock lock(queue_mutex); 25 | return que.empty(); 26 | } 27 | 28 | string QueueRetry::pop() 29 | { 30 | boost::mutex::scoped_lock lock(queue_mutex); 31 | while (que.empty()) 32 | { 33 | cond.wait(lock); 34 | } 35 | string command = que.back(); 36 | que.pop_back(); 37 | return command; 38 | } 39 | 40 | bool QueueRetry::time_wait_pop(string& command, boost::system_time timeout) 41 | { 42 | boost::mutex::scoped_lock lock(queue_mutex); 43 | if (que.empty()) 44 | { 45 | if (!cond.timed_wait(lock, timeout)) 46 | { 47 | return false; 48 | } 49 | } 50 | command = que.back(); 51 | que.pop_back(); 52 | return true; 53 | } 54 | 55 | void QueueRetry::pop_back() 56 | { 57 | boost::mutex::scoped_lock lock(queue_mutex); 58 | while (que.empty()) 59 | { 60 | cond.wait(lock); 61 | } 62 | que.pop_back(); 63 | } 64 | 65 | string QueueRetry::back() 66 | { 67 | boost::mutex::scoped_lock lock(queue_mutex); 68 | while (que.empty()) 69 | { 70 | cond.wait(lock); 71 | } 72 | string command = que.back(); 73 | return command; 74 | } 75 | 76 | int QueueRetry::push(string command) 77 | { 78 | boost::mutex::scoped_lock lock(queue_mutex); 79 | if (que.size() >= MAX_LENGTH_FAILQUEUE) 80 | { 81 | ErrorLog(command); 82 | return 0; 83 | } 84 | cond.notify_all(); 85 | que.push_front(command); 86 | return 1; 87 | } 88 | 89 | void QueueRetry::ErrorLog(string command) 90 | { 91 | ofstream out; 92 | out.open(fileName.c_str(), ofstream::app); 93 | out << command << endl; 94 | out.close(); 95 | } 96 | 97 | void QueueRetry::printdeque() 98 | { 99 | boost::mutex::scoped_lock lock(queue_mutex); 100 | int cre = 0; 101 | int del = 0; 102 | if (que.empty()) 103 | { 104 | cout << "empty retry Queue" << endl; 105 | } else 106 | { 107 | for (std::deque::iterator itrt = que.begin(); itrt < que.end(); itrt++) 108 | { 109 | std::cout << "Queue Retry: " << (*itrt) << "\n"; 110 | if ((*itrt).find("--delete") != string::npos) 111 | { 112 | del++; 113 | } else 114 | { 115 | cre++; 116 | } 117 | } 118 | cout << "Queue Retry: " << endl; 119 | cout << "amount: " << que.size() << "\t"; 120 | cout << "create: " << cre << "\t"; 121 | cout << "delete: " << del << "\t"; 122 | std::cout << endl << endl << endl; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /QueueRetry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: QueueRetry.h 3 | * Author: root 4 | * 5 | * Created on 2009年12月3日, 上午4:45 6 | */ 7 | 8 | #ifndef _QUEUERETRY_H 9 | #define _QUEUERETRY_H 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | #define MAX_LENGTH_FAILQUEUE 5000 17 | 18 | class QueueRetry { 19 | public: 20 | string fileName; //error log Name 21 | int retryInterval; 22 | private: 23 | boost::mutex queue_mutex; 24 | boost::condition_variable cond; 25 | std::deque que; 26 | 27 | public: 28 | QueueRetry(); 29 | string pop(); 30 | bool time_wait_pop(string& command, boost::system_time timeout); 31 | int push(string fe); 32 | bool empty(); 33 | void printdeque(); 34 | string back(); 35 | void pop_back(); 36 | void ErrorLog(string command); 37 | void SetRetryInfo(string file_name ,int time); 38 | }; 39 | typedef boost::shared_ptr ptrQRetry; 40 | 41 | 42 | #endif /* _QUEUERETRY_H */ 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sersync 2 | 3 | Synchronize files and folders between servers -using inotiy and rsync with c++ 4 | 5 | 服务器实时同步文件,服务器镜像解决方案 6 | 7 | 这个工程是原sersync项目在[google code](http://code.google.com/p/sersync/)的拷贝, 在此基础上进行源码分析与注释, 可能会进行二次开发, 仅做学习之用. 8 | 9 | 参考文章 10 | 11 | [Sersync使用指南](http://www.linuxidc.com/Linux/2012-02/53572.htm) 12 | 13 | [git官方文档Issues](https://github.com/xiqingongzi/sersync/issues) 14 | 15 | ------ 16 | 17 | ## 编译步骤 18 | 19 | CentOS7下编译成功. 20 | 21 | 首先下载boost库, 解压, 得到`boost_1_64_0`. 22 | 23 | 将其根目录下的`boost`子目录拷贝到`/usr/local/include`目录下, 作为头文件引用. 24 | 25 | 然后按照[Getting Started on Unix Variants](http://www.boost.org/doc/libs/1_64_0/more/getting_started/unix-variants.html#easy-build-and-install)中的指示安装boost库, 作为共享库so使用, 命令如下 26 | 27 | ``` 28 | $ cd path/to/boost_1_64_0 29 | $ ./bootstrap.sh --help 30 | ## 配置选项, 相当于configure 31 | $ ./bootstrap.sh --prefix=/usr/local 32 | ## 相当于make 33 | $ ./b2 34 | ## 相当于make install 35 | $ ./b2 install 36 | ``` 37 | 38 | 完成后boost库将出现在/usr/local/lib目录下. 然后可以使用`make`进行操作 39 | 40 | ``` 41 | ## 将在build/Release目录下生成.o等中间文件, 并且dist目录下生成sersync2可执行文件 42 | $ make 43 | ## 清理build目录和dist目录下的文件 44 | $ make clean 45 | ``` 46 | 47 | ## 使用方法 48 | 49 | 目前不想写这个, 网上有很多相关教程, 请自行搜索. -------------------------------------------------------------------------------- /SocketInterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "QueueFilter.h" 8 | #include "Initialize.h" 9 | #include "xmlParser.h" 10 | #include "Inotify.h" 11 | #include "SocketInterface.h" 12 | 13 | SocketInterface::SocketInterface() : m_ip("127.0.0.1") 14 | { 15 | 16 | } 17 | 18 | void SocketInterface::XmlParse(std::string config_file_name) 19 | { 20 | XMLNode xMainNode = XMLNode::openFileHelper(config_file_name.c_str(), "head"); 21 | int num = xMainNode.nChildNode("plugin"); 22 | if (num == 0) 23 | { 24 | perror("Error there is no plugin tag in config xml\n"); 25 | exit(1); 26 | } 27 | for (int i = 0; i < num; i++) 28 | { 29 | string name = xMainNode.getChildNode("plugin", i).getAttribute("name"); 30 | if (name == "socket") 31 | { 32 | XMLNode xNode = xMainNode.getChildNode("plugin", i); 33 | m_watch = xNode.getChildNode("localpath").getAttribute("watch"); 34 | m_ip = xNode.getChildNode("localpath").getChildNode("deshost").getAttribute("ip"); 35 | m_port = atoi(xNode.getChildNode("localpath").getChildNode("deshost").getAttribute("port")); 36 | cout << "监控路径: " << m_watch << " ip: " << m_ip << " 端口号: " << m_port << endl; 37 | break; 38 | } 39 | } 40 | } 41 | 42 | int SocketInterface::Execute(Event e) 43 | { 44 | static int i = 1; 45 | int sockfd = 0; 46 | int numbytes = 0; 47 | int flags = 0; 48 | int cycletimes = 0; 49 | char sendBuffer[2048] = "\0"; 50 | fd_set wset; 51 | sprintf(sendBuffer, "%s", e->path.c_str()); 52 | 53 | struct sockaddr_in serv_addr; 54 | serv_addr.sin_family = AF_INET; 55 | serv_addr.sin_port = htons(m_port); 56 | serv_addr.sin_addr.s_addr = inet_addr(m_ip.c_str()); 57 | bzero(&(serv_addr.sin_zero), 8); 58 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 59 | { 60 | close(sockfd); 61 | return 2; 62 | } 63 | 64 | flags = fcntl(sockfd, F_GETFL, 0); 65 | fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置为非阻塞; 66 | do 67 | { 68 | connect(sockfd, (struct sockaddr *) & serv_addr, sizeof (struct sockaddr)); 69 | FD_ZERO(&wset); 70 | FD_SET(sockfd, &wset); 71 | if (select(sockfd + 1, NULL, &wset, NULL, &timeout) <= 0 && cycletimes == 5) 72 | { 73 | close(sockfd); 74 | return 5; 75 | } 76 | numbytes = send(sockfd, sendBuffer, strlen(sendBuffer), MSG_NOSIGNAL); 77 | if (numbytes < 0) 78 | { 79 | usleep(20000); 80 | } 81 | cycletimes++; 82 | } while (numbytes < 0 && cycletimes != 5); 83 | if (numbytes < 0) 84 | { 85 | close(sockfd); 86 | return 4; 87 | } 88 | close(sockfd); 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /SocketInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: SocketInterface.h 3 | * Author: root 4 | * 5 | * Created on 2010年1月23日, 下午11:59 6 | */ 7 | 8 | #ifndef _SOCKETINTERFACE_H 9 | #define _SOCKETINTERFACE_H 10 | #include"Interface.h" 11 | 12 | class SocketInterface : public Interface { 13 | public: 14 | std::string m_ip; 15 | public: 16 | SocketInterface(); 17 | virtual void XmlParse(std::string config_file_name); 18 | virtual int Execute(Event e); 19 | }; 20 | 21 | #endif /* _SOCKETINTERFACE_H */ 22 | 23 | -------------------------------------------------------------------------------- /build/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generals-space/sersync/c8c7098eda67ea001ad72b090a46a0b41f03f96f/build/.gitkeep -------------------------------------------------------------------------------- /dist/Release/GNU-Linux-x86/confxml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include"main.h" 3 | #include "Daemon.h" 4 | #include "CdnRefreshInterface.h" 5 | #include"Inotify.h" 6 | #include"Initialize.h" 7 | #include"QueueFilter.h" 8 | #include"QueueRetry.h" 9 | #include"FileSynchronize.h" 10 | #include"SocketInterface.h" 11 | #include"CommandInterface.h" 12 | //#include"HttpInterface.h" 13 | #include"Interface.h" 14 | #include"main.h" 15 | 16 | #define DEBUG 0 17 | int IN_SYNC = 0; // inotify monitor param:in_create in_close_write 18 | 19 | void DebugStart(int level); 20 | 21 | void FileSynchronizeStart(boost::shared_ptr init); 22 | 23 | void ModuleInterfaceStart(string config_file_name, Interface* module); 24 | 25 | ptrQFilter qf(new QueueFilter); //全局智能指针对象事件过滤队列 26 | 27 | ptrQRetry qr(new QueueRetry); //全局智能指针对象失败事件队列 28 | 29 | int debug_level = 0; //debug等级 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | DebugStart(DEBUG); //debug 开关 0关,3打印信息关开 2非线程 1主函数 34 | boost::shared_ptr init(new Initialize(argc, argv)); //初始化 35 | //开启守护线程 36 | if (init->exec_flag & OPEN_DAEMON) 37 | { 38 | Daemon_Start(); 39 | } 40 | 41 | //开启同步模块 42 | if (init->module == "sersync") 43 | { 44 | FileSynchronizeStart(init); 45 | } 46 | //不执行同步单独开启各插件模块 47 | if (init->module == "other") 48 | { 49 | if (init->exec_flag & REFRESHCDN_MODULE) 50 | { 51 | cout << "启动刷新CDN模块" << endl; 52 | CdnRefreshInterface refresh; 53 | ModuleInterfaceStart(init->config_file_name, &refresh); 54 | 55 | } else if (init->exec_flag & SOCKET_MODULE) 56 | { 57 | cout << "启动SOCKET接口" << endl; 58 | SocketInterface socket; 59 | ModuleInterfaceStart(init->config_file_name, &socket); 60 | } else if (init->exec_flag & COMMAND_MODULE) 61 | { 62 | cout<< "start command interface"<config_file_name,&command); 65 | } 66 | //else if (init->exec_flag & HTTP_MODULE) 67 | //{ 68 | // cout << "启动HTTP接口" << endl; 69 | // HttpInterface http; 70 | // ModuleInterfaceStart( init->config_file_name, &http ); 71 | //} 72 | 73 | } 74 | cout << "模块名称错误" << endl; 75 | exit(1); 76 | 77 | }//end main 78 | 79 | 80 | //======================================================================= 81 | //函数名: FileSynchronizeStart 82 | //作者: zy(zhoukunta@qq.com) 83 | //日期: 2009-01-24 84 | //功能: 启动各插件 85 | //输入参数:config_file_name(string)配置文件名 86 | // module(Interface* )多态调用,各模块的共同基类指针 87 | //返回值: void 88 | //修改记录: 89 | //======================================================================= 90 | void ModuleInterfaceStart(string config_file_name, Interface* module) 91 | { 92 | module->XmlParse(config_file_name); 93 | Inotify inotify(module->m_watch); 94 | while (1) 95 | { 96 | inotify.GetEvents(qf); 97 | while (!qf->empty()) 98 | { 99 | Event e = qf->pop(); 100 | module->Execute(e); 101 | } 102 | if (debug_level & MAIN_INFO) 103 | { 104 | qf->printdeque(); 105 | } 106 | } 107 | } 108 | 109 | //======================================================================= 110 | //函数名: FileSynchronizeStart 111 | //作者: zy(zhoukunta@qq.com) 112 | //日期: 2009-01 113 | //功能: 启动文件同步功能 114 | //输入参数:init(boost::shared_ptr) 指向Initialize 对象的智能指针 115 | //返回值: void 116 | //修改记录: 117 | //======================================================================= 118 | 119 | void FileSynchronizeStart(boost::shared_ptr init) 120 | { 121 | 122 | boost::shared_ptr syn(new FileSynchronize(init, qf, qr)); 123 | cout << "run the sersync: " << endl; 124 | cout << "watch path is: " << syn->watch << endl; 125 | boost::shared_ptr inotify(new Inotify(syn->watch)); 126 | while (1) 127 | { 128 | inotify->GetEvents(qf); 129 | syn->ThreadAwaken(); 130 | if (debug_level & MAIN_INFO) 131 | { 132 | qf->printdeque(); 133 | qr->printdeque(); 134 | } 135 | } 136 | 137 | } 138 | 139 | 140 | //======================================================================= 141 | //函数名: DebugStart 142 | //作者: zy(zhoukunta@qq.com) 143 | //日期: 2009-01 144 | //功能: 开启debug,并按参数开启不同等级的打印信息 145 | //输入参数:level(int) 取值范围(1,2,3) 3是全部开启,2是主函数与子类,1是主函数 146 | //返回值: 无 147 | //修改记录: 148 | //======================================================================= 149 | void DebugStart(int level) 150 | { 151 | switch (level) 152 | { 153 | case 3: 154 | debug_level = debug_level | THREAD_DEBUG; 155 | case 2: 156 | debug_level = debug_level | SUB_CLASS; 157 | case 1: 158 | debug_level = debug_level | MAIN_INFO; 159 | break; 160 | default: 161 | debug_level = 0; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: main.h 3 | * Author: root 4 | * 5 | * Created on 2010年1月11日, 下午3:35 6 | */ 7 | 8 | #ifndef _MAIN_H 9 | #define _MAIN_H 10 | 11 | #define MAIN_INFO 0x01 12 | #define SUB_CLASS 0x01<<1 13 | #define THREAD_DEBUG 0x01<<2 14 | #define INOTIFY_DEBUG 0x01<<3 15 | #define MAX_SIZE 1024 16 | extern int debug_level; 17 | #endif /* _MAIN_H */ 18 | 19 | -------------------------------------------------------------------------------- /nbproject/Makefile-Release.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a -pre and a -post target defined where you can add customized code. 6 | # 7 | # This makefile implements configuration specific macros and targets. 8 | 9 | 10 | # Environment 11 | MKDIR=mkdir 12 | CP=cp 13 | GREP=grep 14 | NM=nm 15 | CCADMIN=CCadmin 16 | RANLIB=ranlib 17 | CC=gcc 18 | CCC=g++ 19 | CXX=g++ 20 | FC=gfortran 21 | AS=as 22 | 23 | # Macros 24 | CND_PLATFORM=GNU-Linux-x86 25 | CND_CONF=Release 26 | CND_DISTDIR=dist 27 | 28 | # Include project Makefile 29 | include Makefile 30 | 31 | # Object Directory 32 | OBJECTDIR=build/${CND_CONF}/${CND_PLATFORM} 33 | 34 | # Object Files 35 | OBJECTFILES= \ 36 | ${OBJECTDIR}/main.o \ 37 | ${OBJECTDIR}/SocketInterface.o \ 38 | ${OBJECTDIR}/xmlParser.o \ 39 | ${OBJECTDIR}/CommandInterface.o \ 40 | ${OBJECTDIR}/Initialize.o \ 41 | ${OBJECTDIR}/Interface.o \ 42 | ${OBJECTDIR}/Inotify.o \ 43 | ${OBJECTDIR}/FileSynchronize.o \ 44 | ${OBJECTDIR}/QueueFilter.o \ 45 | ${OBJECTDIR}/QueueRetry.o \ 46 | ${OBJECTDIR}/CdnRefreshInterface.o 47 | 48 | 49 | # C Compiler Flags 50 | CFLAGS= 51 | 52 | # CC Compiler Flags 53 | CCFLAGS= 54 | CXXFLAGS= 55 | 56 | # Fortran Compiler Flags 57 | FFLAGS= 58 | 59 | # Assembler Flags 60 | ASFLAGS= 61 | 62 | # Link Libraries and Options 63 | # LDLIBSOPTIONS=-Llib -L/usr/local/lib -static -lboost_thread -lboost_regex -lboost_date_time -lpthread 64 | LDLIBSOPTIONS=-Llib -L/lib64 -L/usr/lib64 -L/usr/local/lib -lboost_thread -lboost_regex -lboost_system -lboost_date_time -lpthread 65 | 66 | # Build Targets 67 | .build-conf: ${BUILD_SUBPROJECTS} 68 | "${MAKE}" -f nbproject/Makefile-Release.mk dist/Release/GNU-Linux-x86/sersync2 69 | 70 | dist/Release/GNU-Linux-x86/sersync2: ${OBJECTFILES} 71 | ${MKDIR} -p dist/Release/GNU-Linux-x86 72 | ${LINK.cc} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/sersync2 -s ${OBJECTFILES} ${LDLIBSOPTIONS} 73 | 74 | ${OBJECTDIR}/main.o: main.cpp 75 | ${MKDIR} -p ${OBJECTDIR} 76 | ${RM} $@.d 77 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/main.o main.cpp 78 | 79 | ${OBJECTDIR}/SocketInterface.o: SocketInterface.cpp 80 | ${MKDIR} -p ${OBJECTDIR} 81 | ${RM} $@.d 82 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/SocketInterface.o SocketInterface.cpp 83 | 84 | ${OBJECTDIR}/xmlParser.o: xmlParser.cpp 85 | ${MKDIR} -p ${OBJECTDIR} 86 | ${RM} $@.d 87 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/xmlParser.o xmlParser.cpp 88 | 89 | ${OBJECTDIR}/CommandInterface.o: CommandInterface.cpp 90 | ${MKDIR} -p ${OBJECTDIR} 91 | ${RM} $@.d 92 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/CommandInterface.o CommandInterface.cpp 93 | 94 | ${OBJECTDIR}/Initialize.o: Initialize.cpp 95 | ${MKDIR} -p ${OBJECTDIR} 96 | ${RM} $@.d 97 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/Initialize.o Initialize.cpp 98 | 99 | ${OBJECTDIR}/Interface.o: Interface.cpp 100 | ${MKDIR} -p ${OBJECTDIR} 101 | ${RM} $@.d 102 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/Interface.o Interface.cpp 103 | 104 | ${OBJECTDIR}/Inotify.o: Inotify.cpp 105 | ${MKDIR} -p ${OBJECTDIR} 106 | ${RM} $@.d 107 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/Inotify.o Inotify.cpp 108 | 109 | ${OBJECTDIR}/FileSynchronize.o: FileSynchronize.cpp 110 | ${MKDIR} -p ${OBJECTDIR} 111 | ${RM} $@.d 112 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/FileSynchronize.o FileSynchronize.cpp 113 | 114 | ${OBJECTDIR}/QueueFilter.o: QueueFilter.cpp 115 | ${MKDIR} -p ${OBJECTDIR} 116 | ${RM} $@.d 117 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/QueueFilter.o QueueFilter.cpp 118 | 119 | ${OBJECTDIR}/QueueRetry.o: QueueRetry.cpp 120 | ${MKDIR} -p ${OBJECTDIR} 121 | ${RM} $@.d 122 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/QueueRetry.o QueueRetry.cpp 123 | 124 | ${OBJECTDIR}/CdnRefreshInterface.o: CdnRefreshInterface.cpp 125 | ${MKDIR} -p ${OBJECTDIR} 126 | ${RM} $@.d 127 | $(COMPILE.cc) -O2 -Iinclude -I/usr/local/include -MMD -MP -MF $@.d -o ${OBJECTDIR}/CdnRefreshInterface.o CdnRefreshInterface.cpp 128 | 129 | # Subprojects 130 | .build-subprojects: 131 | 132 | # Clean Targets 133 | .clean-conf: ${CLEAN_SUBPROJECTS} 134 | ${RM} -r build/Release 135 | ${RM} dist/Release/GNU-Linux-x86/sersync2 136 | 137 | # Subprojects 138 | .clean-subprojects: 139 | 140 | # Enable dependency checking 141 | .dep.inc: .depcheck-impl 142 | 143 | include .dep.inc 144 | -------------------------------------------------------------------------------- /nbproject/Makefile-impl.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a pre- and a post- target defined where you can add customization code. 6 | # 7 | # This makefile implements macros and targets common to all configurations. 8 | # 9 | # NOCDDL 10 | 11 | 12 | # Building and Cleaning subprojects are done by default, but can be controlled with the SUB 13 | # macro. If SUB=no, subprojects will not be built or cleaned. The following macro 14 | # statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf 15 | # and .clean-reqprojects-conf unless SUB has the value 'no' 16 | SUB_no=NO 17 | SUBPROJECTS=${SUB_${SUB}} 18 | BUILD_SUBPROJECTS_=.build-subprojects 19 | BUILD_SUBPROJECTS_NO= 20 | BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}} 21 | CLEAN_SUBPROJECTS_=.clean-subprojects 22 | CLEAN_SUBPROJECTS_NO= 23 | CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} 24 | 25 | 26 | # Project Name 27 | PROJECTNAME=sersync 28 | 29 | # Active Configuration 30 | DEFAULTCONF=Release 31 | CONF=${DEFAULTCONF} 32 | 33 | # All Configurations 34 | ALLCONFS=Release 35 | 36 | 37 | # build 38 | .build-impl: .build-pre .validate-impl .depcheck-impl 39 | @#echo "=> Running $@... Configuration=$(CONF)" 40 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf 41 | 42 | 43 | # clean 44 | .clean-impl: .clean-pre .validate-impl .depcheck-impl 45 | @#echo "=> Running $@... Configuration=$(CONF)" 46 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf 47 | 48 | 49 | # clobber 50 | .clobber-impl: .clobber-pre .depcheck-impl 51 | @#echo "=> Running $@..." 52 | for CONF in ${ALLCONFS}; \ 53 | do \ 54 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \ 55 | done 56 | 57 | # all 58 | .all-impl: .all-pre .depcheck-impl 59 | @#echo "=> Running $@..." 60 | for CONF in ${ALLCONFS}; \ 61 | do \ 62 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \ 63 | done 64 | 65 | # build tests 66 | .build-tests-impl: .build-impl .build-tests-pre 67 | @#echo "=> Running $@... Configuration=$(CONF)" 68 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf 69 | 70 | # run tests 71 | .test-impl: .build-tests-impl .test-pre 72 | @#echo "=> Running $@... Configuration=$(CONF)" 73 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf 74 | 75 | # dependency checking support 76 | .depcheck-impl: 77 | @echo "# This code depends on make tool being used" >.dep.inc 78 | @if [ -n "${MAKE_VERSION}" ]; then \ 79 | echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \ 80 | echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \ 81 | echo "include \$${DEPFILES}" >>.dep.inc; \ 82 | echo "endif" >>.dep.inc; \ 83 | else \ 84 | echo ".KEEP_STATE:" >>.dep.inc; \ 85 | echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \ 86 | fi 87 | 88 | # configuration validation 89 | .validate-impl: 90 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 91 | then \ 92 | echo ""; \ 93 | echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \ 94 | echo "See 'make help' for details."; \ 95 | echo "Current directory: " `pwd`; \ 96 | echo ""; \ 97 | fi 98 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 99 | then \ 100 | exit 1; \ 101 | fi 102 | 103 | 104 | # help 105 | .help-impl: .help-pre 106 | @echo "This makefile supports the following configurations:" 107 | @echo " ${ALLCONFS}" 108 | @echo "" 109 | @echo "and the following targets:" 110 | @echo " build (default target)" 111 | @echo " clean" 112 | @echo " clobber" 113 | @echo " all" 114 | @echo " help" 115 | @echo "" 116 | @echo "Makefile Usage:" 117 | @echo " make [CONF=] [SUB=no] build" 118 | @echo " make [CONF=] [SUB=no] clean" 119 | @echo " make [SUB=no] clobber" 120 | @echo " make [SUB=no] all" 121 | @echo " make help" 122 | @echo "" 123 | @echo "Target 'build' will build a specific configuration and, unless 'SUB=no'," 124 | @echo " also build subprojects." 125 | @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no'," 126 | @echo " also clean subprojects." 127 | @echo "Target 'clobber' will remove all built files from all configurations and," 128 | @echo " unless 'SUB=no', also from subprojects." 129 | @echo "Target 'all' will will build all configurations and, unless 'SUB=no'," 130 | @echo " also build subprojects." 131 | @echo "Target 'help' prints this message." 132 | @echo "" 133 | 134 | -------------------------------------------------------------------------------- /nbproject/Makefile-variables.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated - do not edit! 3 | # 4 | # NOCDDL 5 | # 6 | CND_BASEDIR=`pwd` 7 | CND_BUILDDIR=build 8 | CND_DISTDIR=dist 9 | # Release configuration 10 | CND_PLATFORM_Release=GNU-Linux-x86 11 | CND_ARTIFACT_DIR_Release=dist/Release/GNU-Linux-x86 12 | CND_ARTIFACT_NAME_Release=sersync2 13 | CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux-x86/sersync2 14 | CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux-x86/package 15 | CND_PACKAGE_NAME_Release=sersync2.tar 16 | CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux-x86/package/sersync2.tar 17 | -------------------------------------------------------------------------------- /nbproject/Package-Release.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # 4 | # Generated - do not edit! 5 | # 6 | 7 | # Macros 8 | TOP=`pwd` 9 | CND_PLATFORM=GNU-Linux-x86 10 | CND_CONF=Release 11 | CND_DISTDIR=dist 12 | NBTMPDIR=build/${CND_CONF}/${CND_PLATFORM}/tmp-packaging 13 | TMPDIRNAME=tmp-packaging 14 | OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/sersync2 15 | OUTPUT_BASENAME=sersync2 16 | PACKAGE_TOP_DIR=sersync2.4tar/ 17 | 18 | # Functions 19 | function checkReturnCode 20 | { 21 | rc=$? 22 | if [ $rc != 0 ] 23 | then 24 | exit $rc 25 | fi 26 | } 27 | function makeDirectory 28 | # $1 directory path 29 | # $2 permission (optional) 30 | { 31 | mkdir -p "$1" 32 | checkReturnCode 33 | if [ "$2" != "" ] 34 | then 35 | chmod $2 "$1" 36 | checkReturnCode 37 | fi 38 | } 39 | function copyFileToTmpDir 40 | # $1 from-file path 41 | # $2 to-file path 42 | # $3 permission 43 | { 44 | cp "$1" "$2" 45 | checkReturnCode 46 | if [ "$3" != "" ] 47 | then 48 | chmod $3 "$2" 49 | checkReturnCode 50 | fi 51 | } 52 | 53 | # Setup 54 | cd "${TOP}" 55 | mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package 56 | rm -rf ${NBTMPDIR} 57 | mkdir -p ${NBTMPDIR} 58 | 59 | # Copy files and create directories and links 60 | cd "${TOP}" 61 | makeDirectory "${NBTMPDIR}/sersync2.4tar/bin" 62 | copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 63 | 64 | cd "${TOP}" 65 | makeDirectory "${NBTMPDIR}/sersync2.4tar/bin" 66 | copyFileToTmpDir "confxml.xml" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/confxml.xml" 0644 67 | 68 | 69 | # Generate tar file 70 | cd "${TOP}" 71 | rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/sersync2.tar 72 | cd ${NBTMPDIR} 73 | tar -fcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/sersync2.tar * 74 | checkReturnCode 75 | 76 | # Cleanup 77 | cd "${TOP}" 78 | rm -rf ${NBTMPDIR} 79 | -------------------------------------------------------------------------------- /nbproject/configurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CdnRefreshInterface.h 6 | CommandInterface.h 7 | Daemon.h 8 | FileSynchronize.h 9 | Initialize.h 10 | Inotify.h 11 | Interface.h 12 | QueueFilter.h 13 | QueueRetry.h 14 | SocketInterface.h 15 | main.h 16 | xmlParser.h 17 | 18 | 19 | CdnRefreshInterface.cpp 20 | CommandInterface.cpp 21 | FileSynchronize.cpp 22 | Initialize.cpp 23 | Inotify.cpp 24 | Interface.cpp 25 | QueueFilter.cpp 26 | QueueRetry.cpp 27 | SocketInterface.cpp 28 | main.cpp 29 | xmlParser.cpp 30 | 31 | 32 | dist/Release/GNU-Linux-x86/confxml.xml 33 | 34 | 35 | Makefile 36 | 37 | 38 | Makefile 39 | 40 | 41 | 42 | localhost 43 | GNU|GNU 44 | 2 45 | 46 | 47 | 48 | 5 49 | 50 | 51 | 5 52 | 53 | include 54 | /usr/local/include 55 | 56 | 57 | 58 | 5 59 | 60 | 61 | ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/sersync2 62 | 63 | lib 64 | /usr/local/lib 65 | 66 | true 67 | 68 | -static 69 | boost_thread 70 | boost_regex 71 | boost_date_time 72 | PosixThreads 73 | 74 | 75 | 76 | 77 | Tar 78 | -f 79 | sersync2.4tar 80 | 81 | 87 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /nbproject/private/configurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Makefile 4 | 0 5 | 6 | 7 | 8 | GizmoSimple 9 | 10 | 11 | 12 | /root/NetBeansProjects/sersync2/dist/Release/GNU-Linux-x86 13 | true 14 | 2 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /nbproject/private/private.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generals-space/sersync/c8c7098eda67ea001ad72b090a46a0b41f03f96f/nbproject/private/private.properties -------------------------------------------------------------------------------- /nbproject/private/private.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/generals-space/sersync/c8c7098eda67ea001ad72b090a46a0b41f03f96f/nbproject/project.properties -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.cnd.makeproject 4 | 5 | 6 | sersync2 7 | 0 8 | 9 | cpp 10 | h 11 | UTF-8 12 | 13 | 14 | 15 | Release 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /readme-detail.md: -------------------------------------------------------------------------------- 1 | 首先下载boost库, 解压, 得到`boost_1_64_0`. 2 | 3 | 将其根目录下的`boost`子目录拷贝到`/usr/local/include`目录下, 作为头文件引用. 4 | 5 | 按照[Getting Started on Unix Variants](http://www.boost.org/doc/libs/1_64_0/more/getting_started/unix-variants.html#easy-build-and-install)中的指示安装boost库, 命令如下 6 | 7 | ``` 8 | $ cd path/to/boost_1_64_0 9 | $ ./bootstrap.sh --help 10 | ## 配置选项, 相当于configure 11 | $ ./bootstrap.sh --prefix=/usr/local 12 | ## 相当于make 13 | $ ./b2 14 | ## 相当于make install, 完成后boost库将出现在/usr/local/lib目录下. 15 | $ ./b2 install 16 | ``` 17 | 18 | boost库安装完成后再执行make, 出现 19 | 20 | ``` 21 | /usr/bin/ld: cannot find -lpthread 22 | /usr/bin/ld: cannot find -lstdc++ 23 | /usr/bin/ld: cannot find -lm 24 | /usr/bin/ld: cannot find -lc 25 | collect2: error: ld returned 1 exit status 26 | make[2]: *** [dist/Release/GNU-Linux-x86/sersync2] Error 1 27 | make[2]: Leaving directory `/mnt/hgfs/home/Coding/sersync' 28 | make[1]: *** [.build-conf] Error 2 29 | make[1]: Leaving directory `/mnt/hgfs/home/Coding/sersync' 30 | make: *** [.build-impl] Error 2 31 | ``` 32 | 33 | 经过查找, makefile实际生效的是`nbproject/Makefile-Release.mk`, 其中`LDLIBSOPTIONS`选项是编译操作的关键字段. 34 | 35 | 按照[这篇问答](http://bbs.chinaunix.net/thread-4156619-1-1.html) 中7楼的说法, 去掉`-static`参数, 添加`libpthread.so`的位置`-L/lib64`. 36 | 37 | 然后又遇到如下问题 38 | 39 | ``` 40 | /usr/bin/ld: build/Release/GNU-Linux-x86/main.o: undefined reference to symbol '_ZN5boost6system15system_categoryEv' 41 | /usr/bin/ld: note: '_ZN5boost6system15system_categoryEv' is defined in DSO /usr/local/lib/libboost_system.so.1.64.0 so try adding it to the linker command line 42 | /usr/local/lib/libboost_system.so.1.64.0: could not read symbols: Invalid operation 43 | ``` 44 | 45 | 根据stackoverflow[这篇问答](http://stackoverflow.com/questions/38650981/undefined-reference-to-symbol-zn5boost6system15system-categoryev-error)中的最佳答案, 添加了`-lboost_system`参数. 46 | 47 | 再次编译成功. 48 | 49 | 工程原来的`LDLIBSOPTIONS`字段已经注释. -------------------------------------------------------------------------------- /xmlParser.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************/ 2 | /*! \mainpage XMLParser library 3 | * \section intro_sec Introduction 4 | * 5 | * This is a basic XML parser written in ANSI C++ for portability. 6 | * It works by using recursion and a node tree for breaking 7 | * down the elements of an XML document. 8 | * 9 | * @version V2.41 10 | * @author Frank Vanden Berghen 11 | * 12 | * Copyright (c) 2002, Business-Insight 13 | * Business-Insight 14 | * All rights reserved. 15 | * See the file AFPL-license.txt about the licensing terms 16 | * 17 | * \section tutorial First Tutorial 18 | * You can follow a simple Tutorial to know the basics... 19 | * 20 | * \section usage General usage: How to include the XMLParser library inside your project. 21 | * 22 | * The library is composed of two files: xmlParser.cpp and 23 | * xmlParser.h. These are the ONLY 2 files that you need when 24 | * using the library inside your own projects. 25 | * 26 | * All the functions of the library are documented inside the comments of the file 27 | * xmlParser.h. These comments can be transformed in 28 | * full-fledged HTML documentation using the DOXYGEN software: simply type: "doxygen doxy.cfg" 29 | * 30 | * By default, the XMLParser library uses (char*) for string representation.To use the (wchar_t*) 31 | * version of the library, you need to define the "_UNICODE" preprocessor definition variable 32 | * (this is usually done inside your project definition file) (This is done automatically for you 33 | * when using Visual Studio). 34 | * 35 | * \section example Advanced Tutorial and Many Examples of usage. 36 | * 37 | * Some very small introductory examples are described inside the Tutorial file 38 | * xmlParser.html 39 | * 40 | * Some additional small examples are also inside the file xmlTest.cpp 41 | * (for the "char*" version of the library) and inside the file 42 | * xmlTestUnicode.cpp (for the "wchar_t*" 43 | * version of the library). If you have a question, please review these additionnal examples 44 | * before sending an e-mail to the author. 45 | * 46 | * To build the examples: 47 | * - linux/unix: type "make" 48 | * - solaris: type "make -f makefile.solaris" 49 | * - windows: Visual Studio: double-click on xmlParser.dsw 50 | * (under Visual Studio .NET, the .dsp and .dsw files will be automatically converted to .vcproj and .sln files) 51 | * 52 | * In order to build the examples you need some additional files: 53 | * - linux/unix: makefile 54 | * - solaris: makefile.solaris 55 | * - windows: Visual Studio: *.dsp, xmlParser.dsw and also xmlParser.lib and xmlParser.dll 56 | * 57 | * \section debugging Debugging with the XMLParser library 58 | * 59 | * \subsection debugwin Debugging under WINDOWS 60 | * 61 | * Inside Visual C++, the "debug versions" of the memory allocation functions are 62 | * very slow: Do not forget to compile in "release mode" to get maximum speed. 63 | * When I had to debug a software that was using the XMLParser Library, it was usually 64 | * a nightmare because the library was sooOOOoooo slow in debug mode (because of the 65 | * slow memory allocations in Debug mode). To solve this 66 | * problem, during all the debugging session, I am now using a very fast DLL version of the 67 | * XMLParser Library (the DLL is compiled in release mode). Using the DLL version of 68 | * the XMLParser Library allows me to have lightening XML parsing speed even in debug! 69 | * Other than that, the DLL version is useless: In the release version of my tool, 70 | * I always use the normal, ".cpp"-based, XMLParser Library (I simply include the 71 | * xmlParser.cpp and 72 | * xmlParser.h files into the project). 73 | * 74 | * The file XMLNodeAutoexp.txt contains some 75 | * "tweaks" that improve substancially the display of the content of the XMLNode objects 76 | * inside the Visual Studio Debugger. Believe me, once you have seen inside the debugger 77 | * the "smooth" display of the XMLNode objects, you cannot live without it anymore! 78 | * 79 | * \subsection debuglinux Debugging under LINUX/UNIX 80 | * 81 | * The speed of the debug version of the XMLParser library is tolerable so no extra 82 | * work.has been done. 83 | * 84 | ****************************************************************************/ 85 | 86 | #ifndef __INCLUDE_XML_NODE__ 87 | #define __INCLUDE_XML_NODE__ 88 | 89 | #include 90 | 91 | #ifdef _UNICODE 92 | // If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters). 93 | // This is useful when you get error messages like: 94 | // 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *' 95 | // The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable 96 | // must be defined) or utf8-mode(the pre-processor variable must be undefined). 97 | #define _XMLWIDECHAR 98 | #endif 99 | 100 | #if defined(WIN32) || defined(UNDER_CE) || defined(_WIN32) || defined(WIN64) || defined(__BORLANDC__) 101 | // comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) or Borland 102 | #define _XMLWINDOWS 103 | #endif 104 | 105 | #ifdef XMLDLLENTRY 106 | #undef XMLDLLENTRY 107 | #endif 108 | #ifdef _USE_XMLPARSER_DLL 109 | #ifdef _DLL_EXPORTS_ 110 | #define XMLDLLENTRY __declspec(dllexport) 111 | #else 112 | #define XMLDLLENTRY __declspec(dllimport) 113 | #endif 114 | #else 115 | #define XMLDLLENTRY 116 | #endif 117 | 118 | // uncomment the next line if you want no support for wchar_t* (no need for the or libraries anymore to compile) 119 | //#define XML_NO_WIDE_CHAR 120 | 121 | #ifdef XML_NO_WIDE_CHAR 122 | #undef _XMLWINDOWS 123 | #undef _XMLWIDECHAR 124 | #endif 125 | 126 | #ifdef _XMLWINDOWS 127 | #include 128 | #else 129 | #define XMLDLLENTRY 130 | #ifndef XML_NO_WIDE_CHAR 131 | #include // to have 'wcsrtombs' for ANSI version 132 | // to have 'mbsrtowcs' for WIDECHAR version 133 | #endif 134 | #endif 135 | 136 | // Some common types for char set portable code 137 | #ifdef _XMLWIDECHAR 138 | #define _CXML(c) L ## c 139 | #define XMLCSTR const wchar_t * 140 | #define XMLSTR wchar_t * 141 | #define XMLCHAR wchar_t 142 | #else 143 | #define _CXML(c) c 144 | #define XMLCSTR const char * 145 | #define XMLSTR char * 146 | #define XMLCHAR char 147 | #endif 148 | #ifndef FALSE 149 | #define FALSE 0 150 | #endif /* FALSE */ 151 | #ifndef TRUE 152 | #define TRUE 1 153 | #endif /* TRUE */ 154 | 155 | 156 | /// Enumeration for XML parse errors. 157 | 158 | typedef enum XMLError { 159 | eXMLErrorNone = 0, 160 | eXMLErrorMissingEndTag, 161 | eXMLErrorNoXMLTagFound, 162 | eXMLErrorEmpty, 163 | eXMLErrorMissingTagName, 164 | eXMLErrorMissingEndTagName, 165 | eXMLErrorUnmatchedEndTag, 166 | eXMLErrorUnmatchedEndClearTag, 167 | eXMLErrorUnexpectedToken, 168 | eXMLErrorNoElements, 169 | eXMLErrorFileNotFound, 170 | eXMLErrorFirstTagNotFound, 171 | eXMLErrorUnknownCharacterEntity, 172 | eXMLErrorCharacterCodeAbove255, 173 | eXMLErrorCharConversionError, 174 | eXMLErrorCannotOpenWriteFile, 175 | eXMLErrorCannotWriteFile, 176 | 177 | eXMLErrorBase64DataSizeIsNotMultipleOf4, 178 | eXMLErrorBase64DecodeIllegalCharacter, 179 | eXMLErrorBase64DecodeTruncatedData, 180 | eXMLErrorBase64DecodeBufferTooSmall 181 | } XMLError; 182 | 183 | 184 | /// Enumeration used to manage type of data. Use in conjunction with structure XMLNodeContents 185 | 186 | typedef enum XMLElementType { 187 | eNodeChild = 0, 188 | eNodeAttribute = 1, 189 | eNodeText = 2, 190 | eNodeClear = 3, 191 | eNodeNULL = 4 192 | } XMLElementType; 193 | 194 | /// Structure used to obtain error details if the parse fails. 195 | 196 | typedef struct XMLResults { 197 | enum XMLError error; 198 | int nLine, nColumn; 199 | } XMLResults; 200 | 201 | /// Structure for XML clear (unformatted) node (usually comments) 202 | 203 | typedef struct XMLClear { 204 | XMLCSTR lpszValue; 205 | XMLCSTR lpszOpenTag; 206 | XMLCSTR lpszCloseTag; 207 | } XMLClear; 208 | 209 | /// Structure for XML attribute. 210 | 211 | typedef struct XMLAttribute { 212 | XMLCSTR lpszName; 213 | XMLCSTR lpszValue; 214 | } XMLAttribute; 215 | 216 | /// XMLElementPosition are not interchangeable with simple indexes 217 | typedef int XMLElementPosition; 218 | 219 | struct XMLNodeContents; 220 | 221 | /** @defgroup XMLParserGeneral The XML parser */ 222 | 223 | /// Main Class representing a XML node 224 | 225 | /** 226 | * All operations are performed using this class. 227 | * \note The constructors of the XMLNode class are protected, so use instead one of these four methods to get your first instance of XMLNode: 228 | *
    229 | *
  • XMLNode::parseString
  • 230 | *
  • XMLNode::parseFile
  • 231 | *
  • XMLNode::openFileHelper
  • 232 | *
  • XMLNode::createXMLTopNode (or XMLNode::createXMLTopNode_WOSD)
  • 233 | *
*/ 234 | typedef struct XMLDLLENTRY XMLNode { 235 | private: 236 | 237 | struct XMLNodeDataTag; 238 | 239 | /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode 240 | XMLNode(struct XMLNodeDataTag *pParent, XMLSTR lpszName, char isDeclaration); 241 | /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode 242 | XMLNode(struct XMLNodeDataTag * p); 243 | 244 | public: 245 | static XMLCSTR getVersion(); ///< Return the XMLParser library version number 246 | 247 | /** @defgroup conversions Parsing XML files/strings to an XMLNode structure and Rendering XMLNode's to files/string. 248 | * @ingroup XMLParserGeneral 249 | * @{ */ 250 | 251 | /// Parse an XML string and return the root of a XMLNode tree representing the string. 252 | static XMLNode parseString(XMLCSTR lpXMLString, XMLCSTR tag = NULL, XMLResults *pResults = NULL); 253 | /**< The "parseString" function parse an XML string and return the root of a XMLNode tree. The "opposite" of this function is 254 | * the function "createXMLString" that re-creates an XML string from an XMLNode tree. If the XML document is corrupted, the 255 | * "parseString" method will initialize the "pResults" variable with some information that can be used to trace the error. 256 | * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the 257 | * beginning of the "xmlParser.cpp" file. 258 | * 259 | * @param lpXMLString the XML string to parse 260 | * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). 261 | * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. 262 | */ 263 | 264 | /// Parse an XML file and return the root of a XMLNode tree representing the file. 265 | static XMLNode parseFile(XMLCSTR filename, XMLCSTR tag = NULL, XMLResults *pResults = NULL); 266 | /**< The "parseFile" function parse an XML file and return the root of a XMLNode tree. The "opposite" of this function is 267 | * the function "writeToFile" that re-creates an XML file from an XMLNode tree. If the XML document is corrupted, the 268 | * "parseFile" method will initialize the "pResults" variable with some information that can be used to trace the error. 269 | * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the 270 | * beginning of the "xmlParser.cpp" file. 271 | * 272 | * @param filename the path to the XML file to parse 273 | * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). 274 | * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. 275 | */ 276 | 277 | /// Parse an XML file and return the root of a XMLNode tree representing the file. A very crude error checking is made. An attempt to guess the Char Encoding used in the file is made. 278 | static XMLNode openFileHelper(XMLCSTR filename, XMLCSTR tag = NULL); 279 | /**< The "openFileHelper" function reports to the screen all the warnings and errors that occurred during parsing of the XML file. 280 | * This function also tries to guess char Encoding (UTF-8, ASCII or SHIT-JIS) based on the first 200 bytes of the file. Since each 281 | * application has its own way to report and deal with errors, you should rather use the "parseFile" function to parse XML files 282 | * and program yourself thereafter an "error reporting" tailored for your needs (instead of using the very crude "error reporting" 283 | * mechanism included inside the "openFileHelper" function). 284 | * 285 | * If the XML document is corrupted, the "openFileHelper" method will: 286 | * - display an error message on the console (or inside a messageBox for windows). 287 | * - stop execution (exit). 288 | * 289 | * I strongly suggest that you write your own "openFileHelper" method tailored to your needs. If you still want to parse 290 | * the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the beginning of the "xmlParser.cpp" file. 291 | * 292 | * @param filename the path of the XML file to parse. 293 | * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (). 294 | */ 295 | 296 | static XMLCSTR getError(XMLError error); ///< this gives you a user-friendly explanation of the parsing error 297 | 298 | /// Create an XML string starting from the current XMLNode. 299 | XMLSTR createXMLString(int nFormat = 1, int *pnSize = NULL) const; 300 | /**< The returned string should be free'd using the "freeXMLString" function. 301 | * 302 | * If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element 303 | * with appropriate white spaces and carriage returns. if pnSize is given it returns the size in character of the string. */ 304 | 305 | /// Save the content of an xmlNode inside a file 306 | XMLError writeToFile(XMLCSTR filename, 307 | const char *encoding = NULL, 308 | char nFormat = 1) const; 309 | /**< If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element with appropriate white spaces and carriage returns. 310 | * If the global parameter "characterEncoding==encoding_UTF8", then the "encoding" parameter is ignored and always set to "utf-8". 311 | * If the global parameter "characterEncoding==encoding_ShiftJIS", then the "encoding" parameter is ignored and always set to "SHIFT-JIS". 312 | * If "_XMLWIDECHAR=1", then the "encoding" parameter is ignored and always set to "utf-16". 313 | * If no "encoding" parameter is given the "ISO-8859-1" encoding is used. */ 314 | /** @} */ 315 | 316 | /** @defgroup navigate Navigate the XMLNode structure 317 | * @ingroup XMLParserGeneral 318 | * @{ */ 319 | XMLCSTR getName() const; ///< name of the node 320 | XMLCSTR getText(int i = 0) const; ///< return ith text field 321 | int nText() const; ///< nbr of text field 322 | XMLNode getParentNode() const; ///< return the parent node 323 | XMLNode getChildNode(int i = 0) const; ///< return ith child node 324 | XMLNode getChildNode(XMLCSTR name, int i) const; ///< return ith child node with specific name (return an empty node if failing). If i==-1, this returns the last XMLNode with the given name. 325 | XMLNode getChildNode(XMLCSTR name, int *i = NULL) const; ///< return next child node with specific name (return an empty node if failing) 326 | XMLNode getChildNodeWithAttribute(XMLCSTR tagName, 327 | XMLCSTR attributeName, 328 | XMLCSTR attributeValue = NULL, 329 | int *i = NULL) const; ///< return child node with specific name/attribute (return an empty node if failing) 330 | XMLNode getChildNodeByPath(XMLCSTR path, char createNodeIfMissing = 0, XMLCHAR sep = '/'); 331 | ///< return the first child node with specific path 332 | XMLNode getChildNodeByPathNonConst(XMLSTR path, char createNodeIfMissing = 0, XMLCHAR sep = '/'); 333 | ///< return the first child node with specific path. 334 | 335 | int nChildNode(XMLCSTR name) const; ///< return the number of child node with specific name 336 | int nChildNode() const; ///< nbr of child node 337 | XMLAttribute getAttribute(int i = 0) const; ///< return ith attribute 338 | XMLCSTR getAttributeName(int i = 0) const; ///< return ith attribute name 339 | XMLCSTR getAttributeValue(int i = 0) const; ///< return ith attribute value 340 | char isAttributeSet(XMLCSTR name) const; ///< test if an attribute with a specific name is given 341 | XMLCSTR getAttribute(XMLCSTR name, int i) const; ///< return ith attribute content with specific name (return a NULL if failing) 342 | XMLCSTR getAttribute(XMLCSTR name, int *i = NULL) const; ///< return next attribute content with specific name (return a NULL if failing) 343 | int nAttribute() const; ///< nbr of attribute 344 | XMLClear getClear(int i = 0) const; ///< return ith clear field (comments) 345 | int nClear() const; ///< nbr of clear field 346 | XMLNodeContents enumContents(XMLElementPosition i) const; ///< enumerate all the different contents (attribute,child,text, clear) of the current XMLNode. The order is reflecting the order of the original file/string. NOTE: 0 <= i < nElement(); 347 | int nElement() const; ///< nbr of different contents for current node 348 | char isEmpty() const; ///< is this node Empty? 349 | char isDeclaration() const; ///< is this node a declaration 350 | XMLNode deepCopy() const; ///< deep copy (duplicate/clone) a XMLNode 351 | static XMLNode emptyNode(); ///< return XMLNode::emptyXMLNode; 352 | /** @} */ 353 | 354 | ~XMLNode(); 355 | XMLNode(const XMLNode & A); ///< to allow shallow/fast copy: 356 | XMLNode & operator=(const XMLNode & A); ///< to allow shallow/fast copy: 357 | 358 | XMLNode() : d(NULL) { 359 | }; 360 | static XMLNode emptyXMLNode; 361 | static XMLClear emptyXMLClear; 362 | static XMLAttribute emptyXMLAttribute; 363 | 364 | /** @defgroup xmlModify Create or Update the XMLNode structure 365 | * @ingroup XMLParserGeneral 366 | * The functions in this group allows you to create from scratch (or update) a XMLNode structure. Start by creating your top 367 | * node with the "createXMLTopNode" function and then add new nodes with the "addChild" function. The parameter 'pos' gives 368 | * the position where the childNode, the text or the XMLClearTag will be inserted. The default value (pos=-1) inserts at the 369 | * end. The value (pos=0) insert at the beginning (Insertion at the beginning is slower than at the end).
370 | * 371 | * REMARK: 0 <= pos < nChild()+nText()+nClear()
372 | */ 373 | 374 | /** @defgroup creation Creating from scratch a XMLNode structure 375 | * @ingroup xmlModify 376 | * @{ */ 377 | static XMLNode createXMLTopNode(XMLCSTR lpszName, char isDeclaration = FALSE); ///< Create the top node of an XMLNode structure 378 | XMLNode addChild(XMLCSTR lpszName, char isDeclaration = FALSE, XMLElementPosition pos = -1); ///< Add a new child node 379 | XMLNode addChild(XMLNode nodeToAdd, XMLElementPosition pos = -1); ///< If the "nodeToAdd" has some parents, it will be detached from it's parents before being attached to the current XMLNode 380 | XMLAttribute * addAttribute(XMLCSTR lpszName, XMLCSTR lpszValuev); ///< Add a new attribute 381 | XMLCSTR addText(XMLCSTR lpszValue, XMLElementPosition pos = -1); ///< Add a new text content 382 | XMLClear * addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen = NULL, XMLCSTR lpszClose = NULL, XMLElementPosition pos = -1); 383 | /**< Add a new clear tag 384 | * @param lpszOpen default value "" 386 | */ 387 | /** @} */ 388 | 389 | /** @defgroup xmlUpdate Updating Nodes 390 | * @ingroup xmlModify 391 | * Some update functions: 392 | * @{ 393 | */ 394 | XMLCSTR updateName(XMLCSTR lpszName); ///< change node's name 395 | XMLAttribute * updateAttribute(XMLAttribute *newAttribute, XMLAttribute * oldAttribute); ///< if the attribute to update is missing, a new one will be added 396 | XMLAttribute * updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName = NULL, int i = 0); ///< if the attribute to update is missing, a new one will be added 397 | XMLAttribute * updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName, XMLCSTR lpszOldName); ///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added 398 | XMLCSTR updateText(XMLCSTR lpszNewValue, int i = 0); ///< if the text to update is missing, a new one will be added 399 | XMLCSTR updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added 400 | XMLClear * updateClear(XMLCSTR lpszNewContent, int i = 0); ///< if the clearTag to update is missing, a new one will be added 401 | XMLClear * updateClear(XMLClear *newP, XMLClear * oldP); ///< if the clearTag to update is missing, a new one will be added 402 | XMLClear * updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added 403 | /** @} */ 404 | 405 | /** @defgroup xmlDelete Deleting Nodes or Attributes 406 | * @ingroup xmlModify 407 | * Some deletion functions: 408 | * @{ 409 | */ 410 | /// The "deleteNodeContent" function forces the deletion of the content of this XMLNode and the subtree. 411 | void deleteNodeContent(); 412 | /**< \note The XMLNode instances that are referring to the part of the subtree that has been deleted CANNOT be used anymore!!. Unexpected results will occur if you continue using them. */ 413 | void deleteAttribute(int i = 0); ///< Delete the ith attribute of the current XMLNode 414 | void deleteAttribute(XMLCSTR lpszName); ///< Delete the attribute with the given name (the "strcmp" function is used to find the right attribute) 415 | void deleteAttribute(XMLAttribute * anAttribute); ///< Delete the attribute with the name "anAttribute->lpszName" (the "strcmp" function is used to find the right attribute) 416 | void deleteText(int i = 0); ///< Delete the Ith text content of the current XMLNode 417 | void deleteText(XMLCSTR lpszValue); ///< Delete the text content "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the right text) 418 | void deleteClear(int i = 0); ///< Delete the Ith clear tag inside the current XMLNode 419 | void deleteClear(XMLCSTR lpszValue); ///< Delete the clear tag "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the clear tag) 420 | void deleteClear(XMLClear * p); ///< Delete the clear tag "p" inside the current XMLNode (direct "pointer-to-pointer" comparison on the lpszName of the clear tag is used to find the clear tag) 421 | /** @} */ 422 | 423 | /** @defgroup xmlWOSD ???_WOSD functions. 424 | * @ingroup xmlModify 425 | * The strings given as parameters for the "add" and "update" methods that have a name with 426 | * the postfix "_WOSD" (that means "WithOut String Duplication")(for example "addText_WOSD") 427 | * will be free'd by the XMLNode class. For example, it means that this is incorrect: 428 | * \code 429 | * xNode.addText_WOSD("foo"); 430 | * xNode.updateAttribute_WOSD("#newcolor" ,NULL,"color"); 431 | * \endcode 432 | * In opposition, this is correct: 433 | * \code 434 | * xNode.addText("foo"); 435 | * xNode.addText_WOSD(stringDup("foo")); 436 | * xNode.updateAttribute("#newcolor" ,NULL,"color"); 437 | * xNode.updateAttribute_WOSD(stringDup("#newcolor"),NULL,"color"); 438 | * \endcode 439 | * Typically, you will never do: 440 | * \code 441 | * char *b=(char*)malloc(...); 442 | * xNode.addText(b); 443 | * free(b); 444 | * \endcode 445 | * ... but rather: 446 | * \code 447 | * char *b=(char*)malloc(...); 448 | * xNode.addText_WOSD(b); 449 | * \endcode 450 | * ('free(b)' is performed by the XMLNode class) 451 | * @{ */ 452 | static XMLNode createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration = FALSE); ///< Create the top node of an XMLNode structure 453 | XMLNode addChild_WOSD(XMLSTR lpszName, char isDeclaration = FALSE, XMLElementPosition pos = -1); ///< Add a new child node 454 | XMLAttribute * addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValue); ///< Add a new attribute 455 | XMLCSTR addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos = -1); ///< Add a new text content 456 | XMLClear * addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen = NULL, XMLCSTR lpszClose = NULL, XMLElementPosition pos = -1); ///< Add a new clear Tag 457 | 458 | XMLCSTR updateName_WOSD(XMLSTR lpszName); ///< change node's name 459 | XMLAttribute * updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute * oldAttribute); ///< if the attribute to update is missing, a new one will be added 460 | XMLAttribute * updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName = NULL, int i = 0); ///< if the attribute to update is missing, a new one will be added 461 | XMLAttribute * updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName, XMLCSTR lpszOldName); ///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added 462 | XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, int i = 0); ///< if the text to update is missing, a new one will be added 463 | XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added 464 | XMLClear * updateClear_WOSD(XMLSTR lpszNewContent, int i = 0); ///< if the clearTag to update is missing, a new one will be added 465 | XMLClear * updateClear_WOSD(XMLClear *newP, XMLClear * oldP); ///< if the clearTag to update is missing, a new one will be added 466 | XMLClear * updateClear_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added 467 | /** @} */ 468 | 469 | /** @defgroup xmlPosition Position helper functions (use in conjunction with the update&add functions 470 | * @ingroup xmlModify 471 | * These are some useful functions when you want to insert a childNode, a text or a XMLClearTag in the 472 | * middle (at a specified position) of a XMLNode tree already constructed. The value returned by these 473 | * methods is to be used as last parameter (parameter 'pos') of addChild, addText or addClear. 474 | * @{ */ 475 | XMLElementPosition positionOfText(int i = 0) const; 476 | XMLElementPosition positionOfText(XMLCSTR lpszValue) const; 477 | XMLElementPosition positionOfClear(int i = 0) const; 478 | XMLElementPosition positionOfClear(XMLCSTR lpszValue) const; 479 | XMLElementPosition positionOfClear(XMLClear * a) const; 480 | XMLElementPosition positionOfChildNode(int i = 0) const; 481 | XMLElementPosition positionOfChildNode(XMLNode x) const; 482 | XMLElementPosition positionOfChildNode(XMLCSTR name, int i = 0) const; ///< return the position of the ith childNode with the specified name if (name==NULL) return the position of the ith childNode 483 | /** @} */ 484 | 485 | /// Enumeration for XML character encoding. 486 | 487 | typedef enum XMLCharEncoding { 488 | char_encoding_error = 0, 489 | char_encoding_UTF8 = 1, 490 | char_encoding_legacy = 2, 491 | char_encoding_ShiftJIS = 3, 492 | char_encoding_GB2312 = 4, 493 | char_encoding_Big5 = 5, 494 | char_encoding_GBK = 6 // this is actually the same as Big5 495 | } XMLCharEncoding; 496 | 497 | /** \addtogroup conversions 498 | * @{ */ 499 | 500 | /// Sets the global options for the conversions 501 | static char setGlobalOptions(XMLCharEncoding characterEncoding = XMLNode::char_encoding_UTF8, char guessWideCharChars = 1, 502 | char dropWhiteSpace = 1, char removeCommentsInMiddleOfText = 1); 503 | /**< The "setGlobalOptions" function allows you to change four global parameters that affect string & file 504 | * parsing. First of all, you most-probably will never have to change these 3 global parameters. 505 | * 506 | * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in WideChar mode, then the 507 | * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains ASCII 508 | * characters. If this is the case, then the file will be loaded and converted in memory to 509 | * WideChar before being parsed. If 0, no conversion will be performed. 510 | * 511 | * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in ASCII/UTF8/char* mode, then the 512 | * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains WideChar 513 | * characters. If this is the case, then the file will be loaded and converted in memory to 514 | * ASCII/UTF8/char* before being parsed. If 0, no conversion will be performed. 515 | * 516 | * @param characterEncoding This parameter is only meaningful when compiling in char* mode (multibyte character mode). 517 | * In wchar_t* (wide char mode), this parameter is ignored. This parameter should be one of the 518 | * three currently recognized encodings: XMLNode::encoding_UTF8, XMLNode::encoding_ascii, 519 | * XMLNode::encoding_ShiftJIS. 520 | * 521 | * @param dropWhiteSpace In most situations, text fields containing only white spaces (and carriage returns) 522 | * are useless. Even more, these "empty" text fields are annoying because they increase the 523 | * complexity of the user's code for parsing. So, 99% of the time, it's better to drop 524 | * the "empty" text fields. However The XML specification indicates that no white spaces 525 | * should be lost when parsing the file. So to be perfectly XML-compliant, you should set 526 | * dropWhiteSpace=0. A note of caution: if you set "dropWhiteSpace=0", the parser will be 527 | * slower and your code will be more complex. 528 | * 529 | * @param removeCommentsInMiddleOfText To explain this parameter, let's consider this code: 530 | * \code 531 | * XMLNode x=XMLNode::parseString("foobarchu","a"); 532 | * \endcode 533 | * If removeCommentsInMiddleOfText=0, then we will have: 534 | * \code 535 | * x.getText(0) -> "foo" 536 | * x.getText(1) -> "bar" 537 | * x.getText(2) -> "chu" 538 | * x.getClear(0) --> "" 539 | * x.getClear(1) --> "" 540 | * \endcode 541 | * If removeCommentsInMiddleOfText=1, then we will have: 542 | * \code 543 | * x.getText(0) -> "foobar" 544 | * x.getText(1) -> "chu" 545 | * x.getClear(0) --> "" 546 | * \endcode 547 | * 548 | * \return "0" when there are no errors. If you try to set an unrecognized encoding then the return value will be "1" to signal an error. 549 | * 550 | * \note Sometime, it's useful to set "guessWideCharChars=0" to disable any conversion 551 | * because the test to detect the file-type (ASCII/UTF8/char* or WideChar) may fail (rarely). */ 552 | 553 | /// Guess the character encoding of the string (ascii, utf8 or shift-JIS) 554 | static XMLCharEncoding guessCharEncoding(void *buffer, int bufLen, char useXMLEncodingAttribute = 1); 555 | /**< The "guessCharEncoding" function try to guess the character encoding. You most-probably will never 556 | * have to use this function. It then returns the appropriate value of the global parameter 557 | * "characterEncoding" described in the XMLNode::setGlobalOptions. The guess is based on the content of a buffer of length 558 | * "bufLen" bytes that contains the first bytes (minimum 25 bytes; 200 bytes is a good value) of the 559 | * file to be parsed. The XMLNode::openFileHelper function is using this function to automatically compute 560 | * the value of the "characterEncoding" global parameter. There are several heuristics used to do the 561 | * guess. One of the heuristic is based on the "encoding" attribute. The original XML specifications 562 | * forbids to use this attribute to do the guess but you can still use it if you set 563 | * "useXMLEncodingAttribute" to 1 (this is the default behavior and the behavior of most parsers). 564 | * If an inconsistency in the encoding is detected, then the return value is "0". */ 565 | /** @} */ 566 | 567 | private: 568 | // these are functions and structures used internally by the XMLNode class (don't bother about them): 569 | 570 | typedef struct XMLNodeDataTag // to allow shallow copy and "intelligent/smart" pointers (automatic delete): 571 | { 572 | XMLCSTR lpszName; // Element name (=NULL if root) 573 | int nChild, // Number of child nodes 574 | nText, // Number of text fields 575 | nClear, // Number of Clear fields (comments) 576 | nAttribute; // Number of attributes 577 | char isDeclaration; // Whether node is an XML declaration - '' 578 | struct XMLNodeDataTag *pParent; // Pointer to parent element (=NULL if root) 579 | XMLNode *pChild; // Array of child nodes 580 | XMLCSTR *pText; // Array of text fields 581 | XMLClear *pClear; // Array of clear fields 582 | XMLAttribute *pAttribute; // Array of attributes 583 | int *pOrder; // order of the child_nodes,text_fields,clear_fields 584 | int ref_count; // for garbage collection (smart pointers) 585 | } XMLNodeData; 586 | XMLNodeData *d; 587 | 588 | char parseClearTag(void *px, void *pa); 589 | char maybeAddTxT(void *pa, XMLCSTR tokenPStr); 590 | int ParseXMLElement(void *pXML); 591 | void *addToOrder(int memInc, int *_pos, int nc, void *p, int size, XMLElementType xtype); 592 | int indexText(XMLCSTR lpszValue) const; 593 | int indexClear(XMLCSTR lpszValue) const; 594 | XMLNode addChild_priv(int, XMLSTR, char, int); 595 | XMLAttribute * addAttribute_priv(int, XMLSTR, XMLSTR); 596 | XMLCSTR addText_priv(int, XMLSTR, int); 597 | XMLClear * addClear_priv(int, XMLSTR, XMLCSTR, XMLCSTR, int); 598 | void emptyTheNode(char force); 599 | static inline XMLElementPosition findPosition(XMLNodeData *d, int index, XMLElementType xtype); 600 | static int CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat); 601 | static int removeOrderElement(XMLNodeData *d, XMLElementType t, int index); 602 | static void exactMemory(XMLNodeData * d); 603 | static int detachFromParent(XMLNodeData * d); 604 | } XMLNode; 605 | 606 | /// This structure is given by the function XMLNode::enumContents. 607 | 608 | typedef struct XMLNodeContents { 609 | /// This dictates what's the content of the XMLNodeContent 610 | enum XMLElementType etype; 611 | /**< should be an union to access the appropriate data. Compiler does not allow union of object with constructor... too bad. */ 612 | XMLNode child; 613 | XMLAttribute attrib; 614 | XMLCSTR text; 615 | XMLClear clear; 616 | 617 | } XMLNodeContents; 618 | 619 | /** @defgroup StringAlloc String Allocation/Free functions 620 | * @ingroup xmlModify 621 | * @{ */ 622 | /// Duplicate (copy in a new allocated buffer) the source string. 623 | XMLDLLENTRY XMLSTR stringDup(XMLCSTR source, int cbData = -1); 624 | /**< This is 625 | * a very handy function when used with all the "XMLNode::*_WOSD" functions (\link xmlWOSD \endlink). 626 | * @param cbData If !=0 then cbData is the number of chars to duplicate. New strings allocated with 627 | * this function should be free'd using the "freeXMLString" function. */ 628 | 629 | /// to free the string allocated inside the "stringDup" function or the "createXMLString" function. 630 | XMLDLLENTRY void freeXMLString(XMLSTR t); // {free(t);} 631 | /** @} */ 632 | 633 | /** @defgroup atoX ato? like functions 634 | * @ingroup XMLParserGeneral 635 | * The "xmlto?" functions are equivalents to the atoi, atol, atof functions. 636 | * The only difference is: If the variable "xmlString" is NULL, than the return value 637 | * is "defautValue". These 6 functions are only here as "convenience" functions for the 638 | * user (they are not used inside the XMLparser). If you don't need them, you can 639 | * delete them without any trouble. 640 | * 641 | * @{ */ 642 | XMLDLLENTRY char xmltob(XMLCSTR xmlString, char defautValue = 0); 643 | XMLDLLENTRY int xmltoi(XMLCSTR xmlString, int defautValue = 0); 644 | XMLDLLENTRY long xmltol(XMLCSTR xmlString, long defautValue = 0); 645 | XMLDLLENTRY double xmltof(XMLCSTR xmlString, double defautValue = .0); 646 | XMLDLLENTRY XMLCSTR xmltoa(XMLCSTR xmlString, XMLCSTR defautValue = _CXML("")); 647 | XMLDLLENTRY XMLCHAR xmltoc(XMLCSTR xmlString, const XMLCHAR defautValue = _CXML('\0')); 648 | /** @} */ 649 | 650 | /** @defgroup ToXMLStringTool Helper class to create XML files using "printf", "fprintf", "cout",... functions. 651 | * @ingroup XMLParserGeneral 652 | * @{ */ 653 | /// Helper class to create XML files using "printf", "fprintf", "cout",... functions. 654 | 655 | /** The ToXMLStringTool class helps you creating XML files using "printf", "fprintf", "cout",... functions. 656 | * The "ToXMLStringTool" class is processing strings so that all the characters 657 | * &,",',<,> are replaced by their XML equivalent: 658 | * \verbatim &, ", ', <, > \endverbatim 659 | * Using the "ToXMLStringTool class" and the "fprintf function" is THE most efficient 660 | * way to produce VERY large XML documents VERY fast. 661 | * \note If you are creating from scratch an XML file using the provided XMLNode class 662 | * you must not use the "ToXMLStringTool" class (because the "XMLNode" class does the 663 | * processing job for you during rendering).*/ 664 | typedef struct XMLDLLENTRY ToXMLStringTool { 665 | public: 666 | 667 | ToXMLStringTool() : buf(NULL), buflen(0) { 668 | } 669 | ~ToXMLStringTool(); 670 | void freeBuffer(); ///