configparser.C 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. * */
  13. /*
  14. * configparser.C
  15. *
  16. * Created on: Mar 2, 2013
  17. * Author: xaxaxa
  18. */
  19. #include "include/configparser.H"
  20. #include <functional>
  21. #include <sstream>
  22. #define CONCAT(s) (((stringstream&)(stringstream() << s)).str().c_str())
  23. using namespace std;
  24. namespace socketd
  25. {
  26. struct configToken
  27. {
  28. enum types
  29. {
  30. none = 0, t_line, t_beginBlock, t_endBlock
  31. } type;
  32. const char* data;
  33. int datalen;
  34. struct parserInfo
  35. {
  36. int pos;
  37. int line;
  38. } inf;
  39. };
  40. ParserException::ParserException() :
  41. message(strerror(errno)), number(errno) {
  42. }
  43. ParserException::ParserException(int32_t number) :
  44. message(strerror(number)), number(number) {
  45. }
  46. ParserException::ParserException(string message, int32_t number) :
  47. message(message), number(number) {
  48. }
  49. ParserException::~ParserException() throw () {
  50. }
  51. const char* ParserException::what() const throw () {
  52. return message.c_str();
  53. }
  54. class ParserException_internal: public ParserException
  55. {
  56. public:
  57. string message;
  58. int32_t number;
  59. ParserException_internal() {
  60. }
  61. ParserException_internal(int32_t number) :
  62. ParserException(number) {
  63. }
  64. ParserException_internal(string message, int32_t number = 0) :
  65. ParserException(message, number) {
  66. }
  67. ParserException_internal(const configToken::parserInfo& inf, string message, int32_t number =
  68. 0) :
  69. ParserException(CONCAT("line " << inf.line << ": " <<message), number) {
  70. }
  71. ParserException_internal(const configToken& ct, string message, int32_t number = 0) :
  72. ParserException(CONCAT("line " << ct.inf.line << ": " <<message), number) {
  73. }
  74. ~ParserException_internal() throw () {
  75. }
  76. };
  77. inline bool isWhitespace(char ch) {
  78. return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
  79. }
  80. static inline void parseConfig_out(configToken& t, const char* conf, int len, int i, int& last_i,
  81. const function<void(const configToken&)>& cb) {
  82. while (last_i < i && isWhitespace(conf[last_i]))
  83. last_i++;
  84. t.data = conf + last_i;
  85. t.datalen = i - last_i;
  86. cb(t);
  87. last_i = i + 1;
  88. }
  89. void parseConfig(const char* conf, int len, const function<void(const configToken&)>& cb) {
  90. configToken t;
  91. int i = 0;
  92. int last_i = 0;
  93. int line = 1;
  94. //int tmpline = 0;
  95. main_loop: while (i < len) {
  96. if (conf[i] == '{') {
  97. t.type = configToken::t_beginBlock;
  98. goto out;
  99. } else if (conf[i] == '}') {
  100. for (int x = last_i; x < i; x++) {
  101. if (!isWhitespace(conf[x])) throw ParserException_internal( { last_i, line },
  102. "garbage before \"}\". maybe you forgot a \";\"?");
  103. }
  104. t.type = configToken::t_endBlock;
  105. goto out;
  106. } else if (conf[i] == ';') {
  107. t.type = configToken::t_line;
  108. goto out;
  109. } else if (conf[i] == '/') {
  110. goto loop2;
  111. } else if (conf[i] == '\n') {
  112. line++;
  113. }
  114. cont: i++;
  115. }
  116. return;
  117. loop2: i++;
  118. if (i >= len || conf[i] != '/') goto main_loop;
  119. i++;
  120. while (i < len) {
  121. if (conf[i] == '\r' || conf[i] == '\n') break;
  122. i++;
  123. }
  124. last_i = i;
  125. goto main_loop;
  126. out: t.inf.pos = last_i;
  127. t.inf.line = line;
  128. parseConfig_out(t, conf, len, i, last_i, cb);
  129. //line += tmpline;
  130. //tmpline = 0;
  131. goto cont;
  132. }
  133. //returns length of prefix, because index is always 0
  134. static inline int configGetPrefix(const char* data, int len) {
  135. const char* tmp = (const char*) memchr(data, ' ', len);
  136. if (tmp == NULL) return len;
  137. else return tmp - data;
  138. }
  139. static inline int mystrcmp(const char* s1, int l1, const char* s2, int l2) {
  140. if (l1 != l2) return l1 - l2;
  141. return memcmp(s1, s2, l1);
  142. }
  143. static inline void split(const char* s1, int l1, char c,
  144. const function<void(const char*, int)>& cb) {
  145. int i = 0;
  146. while (i < l1) {
  147. const void* tmp = memchr(s1 + i, c, l1 - i);
  148. if (tmp == NULL) break;
  149. int next = ((const char*) tmp) - s1;
  150. cb(s1 + i, next - i);
  151. i = next + 1;
  152. }
  153. cb(s1 + i, l1 - i);
  154. }
  155. static void parseBindingDirective(binding& b, socketd& sd, const configToken& ct) {
  156. int prefLen = configGetPrefix(ct.data, ct.datalen);
  157. if (ct.datalen - prefLen - 1 <= 0) throw ParserException_internal(ct,
  158. "missing parameter in \"" + string(ct.data, prefLen) + "\" binding target");
  159. if (mystrcmp(ct.data, prefLen, "listen", 6) == 0) {
  160. const char* s = ct.data + prefLen + 1;
  161. int len1 = ct.datalen - prefLen - 1;
  162. const char* tmp = (const char*) memchr(s, ':', len1);
  163. if (tmp == NULL) throw ParserException_internal(ct, "expected \":\"");
  164. int x = tmp - s;
  165. for (int i = 0; i < (int) sd.listens.size(); i++) {
  166. //cout << "compared " << sd.listens[i].host << " " << sd.listens[i].port << endl;
  167. if (mystrcmp(sd.listens[i].host.data(), sd.listens[i].host.length(), s, x) == 0
  168. && mystrcmp(sd.listens[i].port.data(), sd.listens[i].port.length(), s + x + 1,
  169. len1 - x - 1) == 0) {
  170. b.listenID = sd.listens[i].id;
  171. goto sssss;
  172. }
  173. }
  174. throw ParserException_internal(ct,
  175. "the specified listen directive \"" + string(s, len1) + "\" was not found");
  176. sssss: b.matchLevel |= binding::match_listenID;
  177. } else if (mystrcmp(ct.data, prefLen, "httppath", 8) == 0) {
  178. b.httpPath = string(ct.data + prefLen + 1, ct.datalen - prefLen - 1);
  179. b.matchLevel |= binding::match_httpPath;
  180. } else if (mystrcmp(ct.data, prefLen, "httphost", 8) == 0) {
  181. b.httpHost = string(ct.data + prefLen + 1, ct.datalen - prefLen - 1);
  182. b.matchLevel |= binding::match_httpHost;
  183. } else throw ParserException_internal(ct,
  184. "expected \"listen\", \"httppath\", or \"httphost\" directive, but got \""
  185. + string(ct.data, prefLen) + "\"");
  186. }
  187. void loadConfig(const char* conf, int len, socketd& sd) {
  188. char state = 0;
  189. vhost* vh;
  190. binding* b;
  191. int maxListenID = 0;
  192. parseConfig(conf, len, [&](const configToken& ct) {
  193. //printf("configToken %i: %s\n",ct.type,string(ct.data,ct.datalen).c_str());
  194. switch(ct.type) {
  195. case configToken::t_beginBlock:
  196. {
  197. switch(state) {
  198. case 0:
  199. {
  200. int prefLen=configGetPrefix(ct.data,ct.datalen);
  201. if(mystrcmp(ct.data,prefLen,"vhost",5)==0) {
  202. state='v';
  203. sd.vhosts.resize(sd.vhosts.size()+1);
  204. vh=&*(sd.vhosts.end()-1);
  205. if (ct.datalen - prefLen - 1 > 0) {
  206. vh->name=string(ct.data + prefLen + 1,ct.datalen - prefLen - 1);
  207. }
  208. } else if(mystrcmp(ct.data,prefLen,"binding",7)==0) {
  209. if (ct.datalen - prefLen - 1 <= 0) throw ParserException_internal(ct, "missing name in \"binding\" block");
  210. sd.extraBindings.resize(sd.extraBindings.size()+1);
  211. b=&*(sd.extraBindings.end()-1);
  212. b->matchLevel=0;
  213. b->vhostName=string(ct.data + prefLen + 1,ct.datalen - prefLen - 1);
  214. state='d';
  215. } else throw ParserException_internal(ct,"expected \"vhost\" or \"binding\" block, but got \""+string(ct.data,prefLen)+"\"");
  216. break;
  217. }
  218. case 'v':
  219. {
  220. int prefLen=configGetPrefix(ct.data,ct.datalen);
  221. if(mystrcmp(ct.data,prefLen,"bindings",8)==0) {
  222. state='b';
  223. } else throw ParserException_internal(ct,"expected \"bindings\" block, but got \""+string(ct.data,prefLen)+"\"");
  224. break;
  225. }
  226. case 'b':
  227. {
  228. vh->bindings.resize(vh->bindings.size()+1);
  229. b=&*(vh->bindings.end()-1);
  230. b->matchLevel=0;
  231. state='c';
  232. break;
  233. }
  234. case 'c':
  235. {
  236. throw ParserException_internal(ct,"unexpected \"{\" when in \"binding\" section");
  237. }
  238. }
  239. break;
  240. }
  241. case configToken::t_endBlock:
  242. {
  243. switch(state) {
  244. case 0:
  245. throw ParserException_internal(ct,"unexpected \"}\"");
  246. case 'v':
  247. state=0; break;
  248. case 'b':
  249. state='v'; break;
  250. case 'c':
  251. state='b'; break;
  252. case 'd':
  253. state=0; break;
  254. }
  255. break;
  256. }
  257. case configToken::t_line:
  258. {
  259. switch(state) {
  260. case 0:
  261. {
  262. int prefLen=configGetPrefix(ct.data,ct.datalen);
  263. if(mystrcmp(ct.data,prefLen,"listen",6)==0) {
  264. sd.listens.resize(sd.listens.size()+1);
  265. listen* l;
  266. l=&*(sd.listens.end()-1);
  267. l->id=(++maxListenID);
  268. int ind=0;
  269. split(ct.data+prefLen,ct.datalen-prefLen,' ',[&](const char* s, int len) {
  270. if(len<=0)return;
  271. //cout << "listen directive token: " << string(s,len) << endl;
  272. switch(ind) {
  273. case 0: //address
  274. {
  275. const char* tmp=(const char*)memchr(s,':',len);
  276. if(tmp==NULL)throw ParserException_internal(ct,"expected \":\"");
  277. int i=tmp - s;
  278. l->host=string(s,i);
  279. l->port=string(s+i+1,len-i-1);
  280. //cout << "host: " << l->host << " port: " << l->port << endl;
  281. break;
  282. }
  283. case 1: //backlog
  284. {
  285. //make a copy because atoi() expects a null byte
  286. string tmp(s,len);
  287. l->backlog=atoi(tmp.c_str());
  288. break;
  289. }
  290. default:
  291. throw ParserException_internal(ct,"trailing garbage in \"listen\" directive");
  292. break;
  293. }
  294. ind++;
  295. });
  296. } else if(mystrcmp(ct.data,prefLen,"ipcbuffersize",13)==0) {
  297. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"ipcbuffersize\" directive");
  298. sd.ipcBufSize=atoi(string(ct.data+prefLen+1,ct.datalen-prefLen-1).c_str());
  299. } else if(mystrcmp(ct.data,prefLen,"threads",7)==0) {
  300. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"threads\" directive");
  301. sd.threads=atoi(string(ct.data+prefLen+1,ct.datalen-prefLen-1).c_str());
  302. } else throw ParserException_internal(ct,"expected \"listen\" or \"ipcbuffersize\" directive, but got \""+string(ct.data,prefLen)+"\"");
  303. break;
  304. }
  305. case 'v':
  306. {
  307. int prefLen=configGetPrefix(ct.data,ct.datalen);
  308. if(mystrcmp(ct.data,prefLen,"exec",4)==0) {
  309. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"exec\" directive");
  310. vh->exepath=string(ct.data+prefLen+1,ct.datalen-prefLen-1);
  311. } else if(mystrcmp(ct.data,prefLen,"shell",5)==0) {
  312. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"shell\" directive");
  313. vh->useShell=ct.data[prefLen+1]=='1';
  314. } else if(mystrcmp(ct.data,prefLen,"preload",7)==0) {
  315. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"preload\" directive");
  316. vh->preload=ct.data[prefLen+1]=='1';
  317. } else if(mystrcmp(ct.data,prefLen,"authcookie",10)==0) {
  318. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"authcookie\" directive");
  319. vh->authCookie=string(ct.data+prefLen+1,ct.datalen-prefLen-1);
  320. } else if(mystrcmp(ct.data,prefLen,"processes",9)==0) {
  321. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"processes\" directive");
  322. vh->processes=atoi(string(ct.data+prefLen+1,ct.datalen-prefLen-1).c_str());
  323. } else if(mystrcmp(ct.data,prefLen,"ipcbuffersize",13)==0) {
  324. if(ct.datalen-prefLen-1<=0) throw ParserException_internal(ct,"missing parameter in \"ipcbuffersize\" directive");
  325. vh->ipcBufSize=atoi(string(ct.data+prefLen+1,ct.datalen-prefLen-1).c_str());
  326. } else throw ParserException_internal(ct,"expected \"exec\", \"shell\", \"preload\", \"authcookie\", \"processes\", or \"ipcbuffersize\" directive, but got \""+string(ct.data,prefLen)+"\"");
  327. break;
  328. }
  329. case 'b':
  330. {
  331. throw ParserException_internal(ct,"unexpected directive when in \"bindings\" section");
  332. break;
  333. }
  334. case 'c':
  335. {
  336. parseBindingDirective(*b,sd,ct);
  337. break;
  338. }
  339. case 'd':
  340. {
  341. parseBindingDirective(*b,sd,ct);
  342. break;
  343. }
  344. }
  345. break;
  346. }
  347. }
  348. });
  349. if (state != 0) throw ParserException_internal("got EOF while searching for matching \"}\"");
  350. }
  351. void reloadConfig(const char* conf, int len, socketd& sd) {
  352. }
  353. }