├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build ├── .gitignore └── CMakeLists.txt ├── data ├── logo └── pic.png ├── lib ├── cJSON.c └── cJSON.h ├── scripts ├── enable_service.sh └── kbpm ├── src ├── ansi_escape.h ├── box_drawing.c ├── box_drawing.h ├── client.c ├── client.h ├── kbpm.c ├── kbpm.h ├── proc_status.c ├── proc_status.h ├── process.c ├── process.h ├── server.c ├── server.h ├── utils.c └── utils.h └── test └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | addons: 5 | apt: 6 | packages: 7 | - cmake 8 | script: cd build && cmake . && make -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kalen Blue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KB-PM 2 | [![Build Status](https://travis-ci.org/nladuo/KB-PM.svg)](https://travis-ci.org/nladuo/KB-PM) 3 | 4 | a CLI tool (like supervisor and pm2) keep a group of programs running continuously. 5 | 6 | ## Screenshot 7 | ![](./data/pic.png)
8 | 9 | ## Installation 10 | ``` shell 11 | git clone https://github.com/nladuo/KB-PM.git 12 | cd KB-PM/build 13 | cmake . # generate Makefile by cmake 14 | make && sudo make install 15 | ``` 16 | ## Example 17 | ``` shell 18 | kbpm service start # start the KB_PM service 19 | chmod +x ../test/test.sh # make test.sh executeable 20 | kbpm start ../test/test.sh # execute test.sh, use absolute path or relative path 21 | tail -f test.log # check out the stdout of test.sh 22 | ``` 23 | ## Log 24 | KB_PM use syslog for logging, you can check the log at /var/log/syslog(for ubuntu) or /var/log/messages(for centos). 25 | 26 | ## TODO 27 | - [x] 1. stop all the child processes when stopping an app. 28 | - [ ] 2. add the log of every app. 29 | - [ ] 3. more specifically show the memory detail. 30 | 31 | ## License 32 | MIT 33 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles/ 3 | cmake_install.cmake 4 | install_manifest.txt 5 | kbpm 6 | Makefile 7 | -------------------------------------------------------------------------------- /build/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (kbpm) 3 | aux_source_directory(../lib DIR_LIBS) 4 | aux_source_directory(../src DIR_SRCS) 5 | add_executable(kbpm ${DIR_LIBS} ${DIR_SRCS}) 6 | 7 | # -lpthread 8 | find_package(Threads REQUIRED) 9 | if(THREADS_HAVE_PTHREAD_ARG) 10 | set_property(TARGET kbpm PROPERTY COMPILE_OPTIONS "-pthread") 11 | set_property(TARGET kbpm PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread") 12 | endif() 13 | if(CMAKE_THREAD_LIBS_INIT) 14 | target_link_libraries(kbpm "${CMAKE_THREAD_LIBS_INIT}") 15 | endif() 16 | 17 | # -lm 18 | find_library(M_LIB m) 19 | target_link_libraries(kbpm ${M_LIB}) 20 | 21 | set(CMAKE_INSTALL_PREFIX /usr/local) 22 | install(PROGRAMS kbpm DESTINATION bin) 23 | 24 | # set(CPACK_GENERATOR "DEB") 25 | # set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kalen Blue") 26 | # set(CPACK_PACKAGE_VERSION "0.1.0") 27 | # include(CPack) -------------------------------------------------------------------------------- /data/logo: -------------------------------------------------------------------------------- 1 | _ ______ ____ __ __ 2 | | |/ / __ ) | _ \| \/ | 3 | | ' /| _ \ | |_) | |\/| | 4 | | . \| |_) | | __/| | | | 5 | |_|\_\____/___|_| |_| |_| 6 | |_____| 7 | -------------------------------------------------------------------------------- /data/pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nladuo/KB-PM/139ce25612b4611f74a4691b554f278b6ca02739/data/pic.png -------------------------------------------------------------------------------- /lib/cJSON.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* cJSON */ 24 | /* JSON parser in C. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "cJSON.h" 34 | 35 | static const char *ep; 36 | 37 | const char *cJSON_GetErrorPtr(void) {return ep;} 38 | 39 | static int cJSON_strcasecmp(const char *s1,const char *s2) 40 | { 41 | if (!s1) return (s1==s2)?0:1;if (!s2) return 1; 42 | for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; 43 | return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); 44 | } 45 | 46 | static void *(*cJSON_malloc)(size_t sz) = malloc; 47 | static void (*cJSON_free)(void *ptr) = free; 48 | 49 | static char* cJSON_strdup(const char* str) 50 | { 51 | size_t len; 52 | char* copy; 53 | 54 | len = strlen(str) + 1; 55 | if (!(copy = (char*)cJSON_malloc(len))) return 0; 56 | memcpy(copy,str,len); 57 | return copy; 58 | } 59 | 60 | void cJSON_InitHooks(cJSON_Hooks* hooks) 61 | { 62 | if (!hooks) { /* Reset hooks */ 63 | cJSON_malloc = malloc; 64 | cJSON_free = free; 65 | return; 66 | } 67 | 68 | cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; 69 | cJSON_free = (hooks->free_fn)?hooks->free_fn:free; 70 | } 71 | 72 | /* Internal constructor. */ 73 | static cJSON *cJSON_New_Item(void) 74 | { 75 | cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); 76 | if (node) memset(node,0,sizeof(cJSON)); 77 | return node; 78 | } 79 | 80 | /* Delete a cJSON structure. */ 81 | void cJSON_Delete(cJSON *c) 82 | { 83 | cJSON *next; 84 | while (c) 85 | { 86 | next=c->next; 87 | if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); 88 | if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); 89 | if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string); 90 | cJSON_free(c); 91 | c=next; 92 | } 93 | } 94 | 95 | /* Parse the input text to generate a number, and populate the result into item. */ 96 | static const char *parse_number(cJSON *item,const char *num) 97 | { 98 | double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; 99 | 100 | if (*num=='-') sign=-1,num++; /* Has sign? */ 101 | if (*num=='0') num++; /* is zero */ 102 | if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ 103 | if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ 104 | if (*num=='e' || *num=='E') /* Exponent? */ 105 | { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ 106 | while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ 107 | } 108 | 109 | n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ 110 | 111 | item->valuedouble=n; 112 | item->valueint=(int)n; 113 | item->type=cJSON_Number; 114 | return num; 115 | } 116 | 117 | static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; } 118 | 119 | typedef struct {char *buffer; int length; int offset; } printbuffer; 120 | 121 | static char* ensure(printbuffer *p,int needed) 122 | { 123 | char *newbuffer;int newsize; 124 | if (!p || !p->buffer) return 0; 125 | needed+=p->offset; 126 | if (needed<=p->length) return p->buffer+p->offset; 127 | 128 | newsize=pow2gt(needed); 129 | newbuffer=(char*)cJSON_malloc(newsize); 130 | if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;} 131 | if (newbuffer) memcpy(newbuffer,p->buffer,p->length); 132 | cJSON_free(p->buffer); 133 | p->length=newsize; 134 | p->buffer=newbuffer; 135 | return newbuffer+p->offset; 136 | } 137 | 138 | static int update(printbuffer *p) 139 | { 140 | char *str; 141 | if (!p || !p->buffer) return 0; 142 | str=p->buffer+p->offset; 143 | return p->offset+strlen(str); 144 | } 145 | 146 | /* Render the number nicely from the given item into a string. */ 147 | static char *print_number(cJSON *item,printbuffer *p) 148 | { 149 | char *str=0; 150 | double d=item->valuedouble; 151 | if (d==0) 152 | { 153 | if (p) str=ensure(p,2); 154 | else str=(char*)cJSON_malloc(2); /* special case for 0. */ 155 | if (str) strcpy(str,"0"); 156 | } 157 | else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) 158 | { 159 | if (p) str=ensure(p,21); 160 | else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ 161 | if (str) sprintf(str,"%d",item->valueint); 162 | } 163 | else 164 | { 165 | if (p) str=ensure(p,64); 166 | else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ 167 | if (str) 168 | { 169 | if (fpclassify(d) != FP_ZERO && !isnormal(d)) sprintf(str,"null"); 170 | else if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) sprintf(str,"%.0f",d); 171 | else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); 172 | else sprintf(str,"%f",d); 173 | } 174 | } 175 | return str; 176 | } 177 | 178 | static unsigned parse_hex4(const char *str) 179 | { 180 | unsigned h=0; 181 | if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; 182 | h=h<<4;str++; 183 | if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; 184 | h=h<<4;str++; 185 | if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; 186 | h=h<<4;str++; 187 | if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; 188 | return h; 189 | } 190 | 191 | /* Parse the input text into an unescaped cstring, and populate item. */ 192 | static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 193 | static const char *parse_string(cJSON *item,const char *str) 194 | { 195 | const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; 196 | if (*str!='\"') {ep=str;return 0;} /* not a string! */ 197 | 198 | while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ 199 | 200 | out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ 201 | if (!out) return 0; 202 | 203 | ptr=str+1;ptr2=out; 204 | while (*ptr!='\"' && *ptr) 205 | { 206 | if (*ptr!='\\') *ptr2++=*ptr++; 207 | else 208 | { 209 | ptr++; 210 | switch (*ptr) 211 | { 212 | case 'b': *ptr2++='\b'; break; 213 | case 'f': *ptr2++='\f'; break; 214 | case 'n': *ptr2++='\n'; break; 215 | case 'r': *ptr2++='\r'; break; 216 | case 't': *ptr2++='\t'; break; 217 | case 'u': /* transcode utf16 to utf8. */ 218 | uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ 219 | 220 | if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ 221 | 222 | if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ 223 | { 224 | if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ 225 | uc2=parse_hex4(ptr+3);ptr+=6; 226 | if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ 227 | uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); 228 | } 229 | 230 | len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; 231 | 232 | switch (len) { 233 | case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 234 | case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 235 | case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; 236 | case 1: *--ptr2 =(uc | firstByteMark[len]); 237 | } 238 | ptr2+=len; 239 | break; 240 | default: *ptr2++=*ptr; break; 241 | } 242 | ptr++; 243 | } 244 | } 245 | *ptr2=0; 246 | if (*ptr=='\"') ptr++; 247 | item->valuestring=out; 248 | item->type=cJSON_String; 249 | return ptr; 250 | } 251 | 252 | /* Render the cstring provided to an escaped version that can be printed. */ 253 | static char *print_string_ptr(const char *str,printbuffer *p) 254 | { 255 | const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token; 256 | 257 | if (!str) 258 | { 259 | if (p) out=ensure(p,3); 260 | else out=(char*)cJSON_malloc(3); 261 | if (!out) return 0; 262 | strcpy(out,"\"\""); 263 | return out; 264 | } 265 | 266 | for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0; 267 | if (!flag) 268 | { 269 | len=ptr-str; 270 | if (p) out=ensure(p,len+3); 271 | else out=(char*)cJSON_malloc(len+3); 272 | if (!out) return 0; 273 | ptr2=out;*ptr2++='\"'; 274 | strcpy(ptr2,str); 275 | ptr2[len]='\"'; 276 | ptr2[len+1]=0; 277 | return out; 278 | } 279 | 280 | ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} 281 | 282 | if (p) out=ensure(p,len+3); 283 | else out=(char*)cJSON_malloc(len+3); 284 | if (!out) return 0; 285 | 286 | ptr2=out;ptr=str; 287 | *ptr2++='\"'; 288 | while (*ptr) 289 | { 290 | if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; 291 | else 292 | { 293 | *ptr2++='\\'; 294 | switch (token=*ptr++) 295 | { 296 | case '\\': *ptr2++='\\'; break; 297 | case '\"': *ptr2++='\"'; break; 298 | case '\b': *ptr2++='b'; break; 299 | case '\f': *ptr2++='f'; break; 300 | case '\n': *ptr2++='n'; break; 301 | case '\r': *ptr2++='r'; break; 302 | case '\t': *ptr2++='t'; break; 303 | default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ 304 | } 305 | } 306 | } 307 | *ptr2++='\"';*ptr2++=0; 308 | return out; 309 | } 310 | /* Invote print_string_ptr (which is useful) on an item. */ 311 | static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);} 312 | 313 | /* Predeclare these prototypes. */ 314 | static const char *parse_value(cJSON *item,const char *value); 315 | static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p); 316 | static const char *parse_array(cJSON *item,const char *value); 317 | static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p); 318 | static const char *parse_object(cJSON *item,const char *value); 319 | static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p); 320 | 321 | /* Utility to jump whitespace and cr/lf */ 322 | static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} 323 | 324 | /* Parse an object - create a new root, and populate. */ 325 | cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) 326 | { 327 | const char *end=0; 328 | cJSON *c=cJSON_New_Item(); 329 | ep=0; 330 | if (!c) return 0; /* memory fail */ 331 | 332 | end=parse_value(c,skip(value)); 333 | if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ 334 | 335 | /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 336 | if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} 337 | if (return_parse_end) *return_parse_end=end; 338 | return c; 339 | } 340 | /* Default options for cJSON_Parse */ 341 | cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} 342 | 343 | /* Render a cJSON item/entity/structure to text. */ 344 | char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);} 345 | char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);} 346 | 347 | char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt) 348 | { 349 | printbuffer p; 350 | p.buffer=(char*)cJSON_malloc(prebuffer); 351 | p.length=prebuffer; 352 | p.offset=0; 353 | return print_value(item,0,fmt,&p); 354 | } 355 | 356 | 357 | /* Parser core - when encountering text, process appropriately. */ 358 | static const char *parse_value(cJSON *item,const char *value) 359 | { 360 | if (!value) return 0; /* Fail on null. */ 361 | if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } 362 | if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } 363 | if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } 364 | if (*value=='\"') { return parse_string(item,value); } 365 | if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } 366 | if (*value=='[') { return parse_array(item,value); } 367 | if (*value=='{') { return parse_object(item,value); } 368 | 369 | ep=value;return 0; /* failure. */ 370 | } 371 | 372 | /* Render a value to text. */ 373 | static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p) 374 | { 375 | char *out=0; 376 | if (!item) return 0; 377 | if (p) 378 | { 379 | switch ((item->type)&255) 380 | { 381 | case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;} 382 | case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;} 383 | case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;} 384 | case cJSON_Number: out=print_number(item,p);break; 385 | case cJSON_String: out=print_string(item,p);break; 386 | case cJSON_Array: out=print_array(item,depth,fmt,p);break; 387 | case cJSON_Object: out=print_object(item,depth,fmt,p);break; 388 | } 389 | } 390 | else 391 | { 392 | switch ((item->type)&255) 393 | { 394 | case cJSON_NULL: out=cJSON_strdup("null"); break; 395 | case cJSON_False: out=cJSON_strdup("false");break; 396 | case cJSON_True: out=cJSON_strdup("true"); break; 397 | case cJSON_Number: out=print_number(item,0);break; 398 | case cJSON_String: out=print_string(item,0);break; 399 | case cJSON_Array: out=print_array(item,depth,fmt,0);break; 400 | case cJSON_Object: out=print_object(item,depth,fmt,0);break; 401 | } 402 | } 403 | return out; 404 | } 405 | 406 | /* Build an array from input text. */ 407 | static const char *parse_array(cJSON *item,const char *value) 408 | { 409 | cJSON *child; 410 | if (*value!='[') {ep=value;return 0;} /* not an array! */ 411 | 412 | item->type=cJSON_Array; 413 | value=skip(value+1); 414 | if (*value==']') return value+1; /* empty array. */ 415 | 416 | item->child=child=cJSON_New_Item(); 417 | if (!item->child) return 0; /* memory fail */ 418 | value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ 419 | if (!value) return 0; 420 | 421 | while (*value==',') 422 | { 423 | cJSON *new_item; 424 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 425 | child->next=new_item;new_item->prev=child;child=new_item; 426 | value=skip(parse_value(child,skip(value+1))); 427 | if (!value) return 0; /* memory fail */ 428 | } 429 | 430 | if (*value==']') return value+1; /* end of array */ 431 | ep=value;return 0; /* malformed. */ 432 | } 433 | 434 | /* Render an array to text */ 435 | static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p) 436 | { 437 | char **entries; 438 | char *out=0,*ptr,*ret;int len=5; 439 | cJSON *child=item->child; 440 | int numentries=0,i=0,fail=0; 441 | size_t tmplen=0; 442 | 443 | /* How many entries in the array? */ 444 | while (child) numentries++,child=child->next; 445 | /* Explicitly handle numentries==0 */ 446 | if (!numentries) 447 | { 448 | if (p) out=ensure(p,3); 449 | else out=(char*)cJSON_malloc(3); 450 | if (out) strcpy(out,"[]"); 451 | return out; 452 | } 453 | 454 | if (p) 455 | { 456 | /* Compose the output array. */ 457 | i=p->offset; 458 | ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++; 459 | child=item->child; 460 | while (child && !fail) 461 | { 462 | print_value(child,depth+1,fmt,p); 463 | p->offset=update(p); 464 | if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;} 465 | child=child->next; 466 | } 467 | ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0; 468 | out=(p->buffer)+i; 469 | } 470 | else 471 | { 472 | /* Allocate an array to hold the values for each */ 473 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 474 | if (!entries) return 0; 475 | memset(entries,0,numentries*sizeof(char*)); 476 | /* Retrieve all the results: */ 477 | child=item->child; 478 | while (child && !fail) 479 | { 480 | ret=print_value(child,depth+1,fmt,0); 481 | entries[i++]=ret; 482 | if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; 483 | child=child->next; 484 | } 485 | 486 | /* If we didn't fail, try to malloc the output string */ 487 | if (!fail) out=(char*)cJSON_malloc(len); 488 | /* If that fails, we fail. */ 489 | if (!out) fail=1; 490 | 491 | /* Handle failure. */ 492 | if (fail) 493 | { 494 | for (i=0;itype=cJSON_Object; 521 | value=skip(value+1); 522 | if (*value=='}') return value+1; /* empty array. */ 523 | 524 | item->child=child=cJSON_New_Item(); 525 | if (!item->child) return 0; 526 | value=skip(parse_string(child,skip(value))); 527 | if (!value) return 0; 528 | child->string=child->valuestring;child->valuestring=0; 529 | if (*value!=':') {ep=value;return 0;} /* fail! */ 530 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 531 | if (!value) return 0; 532 | 533 | while (*value==',') 534 | { 535 | cJSON *new_item; 536 | if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ 537 | child->next=new_item;new_item->prev=child;child=new_item; 538 | value=skip(parse_string(child,skip(value+1))); 539 | if (!value) return 0; 540 | child->string=child->valuestring;child->valuestring=0; 541 | if (*value!=':') {ep=value;return 0;} /* fail! */ 542 | value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ 543 | if (!value) return 0; 544 | } 545 | 546 | if (*value=='}') return value+1; /* end of array */ 547 | ep=value;return 0; /* malformed. */ 548 | } 549 | 550 | /* Render an object to text. */ 551 | static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p) 552 | { 553 | char **entries=0,**names=0; 554 | char *out=0,*ptr,*ret,*str;int len=7,i=0,j; 555 | cJSON *child=item->child; 556 | int numentries=0,fail=0; 557 | size_t tmplen=0; 558 | /* Count the number of entries. */ 559 | while (child) numentries++,child=child->next; 560 | /* Explicitly handle empty object case */ 561 | if (!numentries) 562 | { 563 | if (p) out=ensure(p,fmt?depth+4:3); 564 | else out=(char*)cJSON_malloc(fmt?depth+4:3); 565 | if (!out) return 0; 566 | ptr=out;*ptr++='{'; 567 | if (fmt) {*ptr++='\n';for (i=0;ioffset; 575 | len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0; 576 | *ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len; 577 | child=item->child;depth++; 578 | while (child) 579 | { 580 | if (fmt) 581 | { 582 | ptr=ensure(p,depth); if (!ptr) return 0; 583 | for (j=0;joffset+=depth; 585 | } 586 | print_string_ptr(child->string,p); 587 | p->offset=update(p); 588 | 589 | len=fmt?2:1; 590 | ptr=ensure(p,len); if (!ptr) return 0; 591 | *ptr++=':';if (fmt) *ptr++='\t'; 592 | p->offset+=len; 593 | 594 | print_value(child,depth,fmt,p); 595 | p->offset=update(p); 596 | 597 | len=(fmt?1:0)+(child->next?1:0); 598 | ptr=ensure(p,len+1); if (!ptr) return 0; 599 | if (child->next) *ptr++=','; 600 | if (fmt) *ptr++='\n';*ptr=0; 601 | p->offset+=len; 602 | child=child->next; 603 | } 604 | ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0; 605 | if (fmt) for (i=0;ibuffer)+i; 608 | } 609 | else 610 | { 611 | /* Allocate space for the names and the objects */ 612 | entries=(char**)cJSON_malloc(numentries*sizeof(char*)); 613 | if (!entries) return 0; 614 | names=(char**)cJSON_malloc(numentries*sizeof(char*)); 615 | if (!names) {cJSON_free(entries);return 0;} 616 | memset(entries,0,sizeof(char*)*numentries); 617 | memset(names,0,sizeof(char*)*numentries); 618 | 619 | /* Collect all the results into our arrays: */ 620 | child=item->child;depth++;if (fmt) len+=depth; 621 | while (child && !fail) 622 | { 623 | names[i]=str=print_string_ptr(child->string,0); 624 | entries[i++]=ret=print_value(child,depth,fmt,0); 625 | if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; 626 | child=child->next; 627 | } 628 | 629 | /* Try to allocate the output string */ 630 | if (!fail) out=(char*)cJSON_malloc(len); 631 | if (!out) fail=1; 632 | 633 | /* Handle failure */ 634 | if (fail) 635 | { 636 | for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} 663 | cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} 664 | cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} 665 | int cJSON_HasObjectItem(cJSON *object,const char *string) { 666 | cJSON *c=object->child; 667 | while (c ) 668 | { 669 | if(cJSON_strcasecmp(c->string,string)==0){ 670 | return 1; 671 | } 672 | c=c->next; 673 | } 674 | return 0; 675 | } 676 | 677 | /* Utility for array list handling. */ 678 | static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} 679 | /* Utility for handling references. */ 680 | static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} 681 | 682 | /* Add item to array/object. */ 683 | void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} 684 | void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} 685 | void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);} 686 | void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} 687 | void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} 688 | 689 | cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; 690 | if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} 691 | void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} 692 | cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} 693 | void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} 694 | 695 | /* Replace array/object items with new ones. */ 696 | void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;} 697 | newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;} 698 | void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; 699 | newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; 700 | if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} 701 | void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} 702 | 703 | /* Create basic types: */ 704 | cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} 705 | cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} 706 | cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} 707 | cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} 708 | cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} 709 | cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} 710 | cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} 711 | cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} 712 | 713 | /* Create Arrays: */ 714 | cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 715 | cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 716 | cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 717 | cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} 718 | 719 | /* Duplication */ 720 | cJSON *cJSON_Duplicate(cJSON *item,int recurse) 721 | { 722 | cJSON *newitem,*cptr,*nptr=0,*newchild; 723 | /* Bail on bad ptr */ 724 | if (!item) return 0; 725 | /* Create new item */ 726 | newitem=cJSON_New_Item(); 727 | if (!newitem) return 0; 728 | /* Copy over all vars */ 729 | newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; 730 | if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} 731 | if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} 732 | /* If non-recursive, then we're done! */ 733 | if (!recurse) return newitem; 734 | /* Walk the ->next chain for the child. */ 735 | cptr=item->child; 736 | while (cptr) 737 | { 738 | newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ 739 | if (!newchild) {cJSON_Delete(newitem);return 0;} 740 | if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 741 | else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ 742 | cptr=cptr->next; 743 | } 744 | return newitem; 745 | } 746 | 747 | void cJSON_Minify(char *json) 748 | { 749 | char *into=json; 750 | while (*json) 751 | { 752 | if (*json==' ') json++; 753 | else if (*json=='\t') json++; /* Whitespace characters. */ 754 | else if (*json=='\r') json++; 755 | else if (*json=='\n') json++; 756 | else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; /* double-slash comments, to end of line. */ 757 | else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} /* multiline comments. */ 758 | else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} /* string literals, which are \" sensitive. */ 759 | else *into++=*json++; /* All other characters. */ 760 | } 761 | *into=0; /* and null-terminate. */ 762 | } 763 | -------------------------------------------------------------------------------- /lib/cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Dave Gamble 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | /* cJSON Types: */ 32 | #define cJSON_False 0 33 | #define cJSON_True 1 34 | #define cJSON_NULL 2 35 | #define cJSON_Number 3 36 | #define cJSON_String 4 37 | #define cJSON_Array 5 38 | #define cJSON_Object 6 39 | 40 | #define cJSON_IsReference 256 41 | #define cJSON_StringIsConst 512 42 | 43 | /* The cJSON structure: */ 44 | typedef struct cJSON { 45 | struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 46 | struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 47 | 48 | int type; /* The type of the item, as above. */ 49 | 50 | char *valuestring; /* The item's string, if type==cJSON_String */ 51 | int valueint; /* The item's number, if type==cJSON_Number */ 52 | double valuedouble; /* The item's number, if type==cJSON_Number */ 53 | 54 | char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 55 | } cJSON; 56 | 57 | typedef struct cJSON_Hooks { 58 | void *(*malloc_fn)(size_t sz); 59 | void (*free_fn)(void *ptr); 60 | } cJSON_Hooks; 61 | 62 | /* Supply malloc, realloc and free functions to cJSON */ 63 | extern void cJSON_InitHooks(cJSON_Hooks* hooks); 64 | 65 | 66 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ 67 | extern cJSON *cJSON_Parse(const char *value); 68 | /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ 69 | extern char *cJSON_Print(cJSON *item); 70 | /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ 71 | extern char *cJSON_PrintUnformatted(cJSON *item); 72 | /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ 73 | extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt); 74 | /* Delete a cJSON entity and all subentities. */ 75 | extern void cJSON_Delete(cJSON *c); 76 | 77 | /* Returns the number of items in an array (or object). */ 78 | extern int cJSON_GetArraySize(cJSON *array); 79 | /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ 80 | extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); 81 | /* Get item "string" from object. Case insensitive. */ 82 | extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); 83 | extern int cJSON_HasObjectItem(cJSON *object,const char *string); 84 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 85 | extern const char *cJSON_GetErrorPtr(void); 86 | 87 | /* These calls create a cJSON item of the appropriate type. */ 88 | extern cJSON *cJSON_CreateNull(void); 89 | extern cJSON *cJSON_CreateTrue(void); 90 | extern cJSON *cJSON_CreateFalse(void); 91 | extern cJSON *cJSON_CreateBool(int b); 92 | extern cJSON *cJSON_CreateNumber(double num); 93 | extern cJSON *cJSON_CreateString(const char *string); 94 | extern cJSON *cJSON_CreateArray(void); 95 | extern cJSON *cJSON_CreateObject(void); 96 | 97 | /* These utilities create an Array of count items. */ 98 | extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); 99 | extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); 100 | extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); 101 | extern cJSON *cJSON_CreateStringArray(const char **strings,int count); 102 | 103 | /* Append item to the specified array/object. */ 104 | extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); 105 | extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); 106 | extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ 107 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 108 | extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 109 | extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); 110 | 111 | /* Remove/Detatch items from Arrays/Objects. */ 112 | extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); 113 | extern void cJSON_DeleteItemFromArray(cJSON *array,int which); 114 | extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); 115 | extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); 116 | 117 | /* Update array items. */ 118 | extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */ 119 | extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); 120 | extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 121 | 122 | /* Duplicate a cJSON item */ 123 | extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); 124 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 125 | need to be released. With recurse!=0, it will duplicate any children connected to the item. 126 | The item->next and ->prev pointers are always zero on return from Duplicate. */ 127 | 128 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 129 | extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); 130 | 131 | extern void cJSON_Minify(char *json); 132 | 133 | /* Macros for creating things quickly. */ 134 | #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) 135 | #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) 136 | #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) 137 | #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) 138 | #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) 139 | #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) 140 | 141 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 142 | #define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) 143 | #define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) 144 | 145 | /* Macro for iterating over an array */ 146 | #define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) 147 | 148 | #ifdef __cplusplus 149 | } 150 | #endif 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /scripts/enable_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Description: Add kbpm to system service 3 | #Author: Kalen Blue 4 | 5 | sudo cp kbpm /etc/init.d/ 6 | 7 | # for centos 8 | if [ -f /etc/redhat-release ]; then 9 | sudo chkconfig --add kbpm 10 | fi 11 | 12 | # for ubuntu 13 | if [ -f /etc/lsb-release ]; then 14 | sudo update-rc.d kbpm defaults 15 | fi 16 | -------------------------------------------------------------------------------- /scripts/kbpm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # chkconfig: 35 90 12 4 | # description: KB-PM service 5 | # 6 | KBPM_PATH=/usr/local/bin 7 | export HOME=/root 8 | 9 | # Start the service 10 | start() { 11 | $KBPM_PATH/kbpm service start 12 | } 13 | 14 | # Stop the service 15 | stop() { 16 | pkill kbpm 17 | $KBPM_PATH/kbpm service clean 18 | } 19 | 20 | # Show the service status 21 | status() { 22 | $KBPM_PATH/kbpm status 23 | } 24 | 25 | ### main logic ### 26 | case "$1" in 27 | start) 28 | start 29 | ;; 30 | stop) 31 | stop 32 | ;; 33 | status) 34 | status 35 | ;; 36 | restart) 37 | stop 38 | start 39 | ;; 40 | *) 41 | echo $"Usage: $0 {start|stop|restart|status}" 42 | exit 1 43 | esac 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /src/ansi_escape.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_ANSI_COLOR_H 26 | #define KBPM_ANSI_COLOR_H 27 | 28 | /*change the color in cli.*/ 29 | #define ANSI_COLOR_GRAY "\x1b[30m" 30 | #define ANSI_COLOR_RED "\x1b[31m" 31 | #define ANSI_COLOR_GREEN "\x1b[32m" 32 | #define ANSI_COLOR_CYAN "\x1b[36m" 33 | #define ANSI_COLOR_RESET "\x1b[0m" 34 | /*change the font in cli.*/ 35 | #define ANSI_FONT_BOLD "\x1b[1m" 36 | #define ANSI_FONT_FAINT "\x1b[2m" 37 | #define ANSI_FONT_ITALIC "\x1b[3m" 38 | 39 | #endif /*KBPM_ANSI_COLOR_H*/ -------------------------------------------------------------------------------- /src/box_drawing.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "box_drawing.h" 30 | #include "ansi_escape.h" 31 | 32 | /*get the columns of strlen maximum */ 33 | void get_varible_strlen_max(int var_strlen_max[], char box_line_list[][VAR_NUM][STR_BUFFER_SIZE], int list_count); 34 | 35 | /*print the box.*/ 36 | void print_box(char box_line_list[][VAR_NUM][STR_BUFFER_SIZE], int list_count); 37 | 38 | /*print a box character.*/ 39 | void print_ch(char* ch); 40 | 41 | /*print letter.*/ 42 | void print_box_letters(char box_line[VAR_NUM][STR_BUFFER_SIZE], int var_strlen_max[], int all_bold); 43 | 44 | /*print the top of box.*/ 45 | void print_top_line(int[]); 46 | 47 | /*print the middle of box.*/ 48 | void print_mid_line(int[]); 49 | 50 | /*print the bottom of box.*/ 51 | void print_bot_line(int[]); 52 | 53 | /*according to sec, turn it into minutes, hours or days.*/ 54 | void turn_second(time_t sec, char* buffer); 55 | 56 | void print_process_list_box(process_s process_list[], int list_count) 57 | { 58 | int i; 59 | time_t now; 60 | process_s *p; 61 | char box_line_list[LIST_SIZE][VAR_NUM][STR_BUFFER_SIZE]= {{" App name "," id ", " pid ", " status ", " restart ", " uptime "," memory "}}; 62 | for(i = 0; i < list_count; i++) 63 | { 64 | p = &process_list[i]; 65 | sprintf(box_line_list[i + 1][0], " %s ", p->app_name); 66 | sprintf(box_line_list[i + 1][1], " %d ", p->id); 67 | sprintf(box_line_list[i + 1][2], " %d ", p->pid); 68 | if(p->is_running) 69 | { 70 | time(&now); 71 | sprintf(box_line_list[i + 1][3], " %s ", "online"); 72 | turn_second(now - p->start_time, box_line_list[i + 1][5]); 73 | /*sprintf(box_line_list[i + 1][5], " %lds ", (now - p->start_time));*/ 74 | sprintf(box_line_list[i + 1][6], " %s ", p->memory); 75 | } 76 | else 77 | { 78 | sprintf(box_line_list[i + 1][3], " %s ", "stopped"); 79 | strcpy(box_line_list[i + 1][5], " 0s "); 80 | strcpy(box_line_list[i + 1][6], " 0 B "); 81 | } 82 | sprintf(box_line_list[i + 1][4], " %d ", p->restart_times); 83 | } 84 | print_box(box_line_list, list_count + 1); 85 | } 86 | 87 | void turn_second(time_t sec, char* buffer) 88 | { 89 | if(sec < 60) 90 | { 91 | /*how many seconds.*/ 92 | sprintf(buffer, " %lds ", sec); 93 | } 94 | else if(sec < 60 * 60) 95 | { 96 | /*how many minutes.*/ 97 | sprintf(buffer, " %ldm ", sec / 60); 98 | } 99 | else if(sec < 60 * 60 * 24) 100 | { 101 | /*how many hours.*/ 102 | sprintf(buffer, " %ldh ", sec / 60 / 60); 103 | } 104 | else 105 | { 106 | /*how many days.*/ 107 | sprintf(buffer, " %ldd ", sec / 60 / 60 / 24); 108 | } 109 | 110 | } 111 | 112 | void print_box(char box_line_list[][VAR_NUM][STR_BUFFER_SIZE], int list_count) 113 | { 114 | int i; 115 | int var_strlen_max[VAR_NUM]; 116 | get_varible_strlen_max(var_strlen_max, box_line_list, list_count); 117 | print_top_line(var_strlen_max); 118 | for (i = 0; i < list_count; i++) 119 | { 120 | if (i) 121 | { 122 | print_box_letters(box_line_list[i], var_strlen_max, 0); 123 | } 124 | else 125 | { 126 | print_box_letters(box_line_list[i], var_strlen_max, 1); 127 | } 128 | 129 | if (i < list_count - 1) 130 | { 131 | print_mid_line(var_strlen_max); 132 | } 133 | } 134 | print_bot_line(var_strlen_max); 135 | } 136 | 137 | 138 | void print_box_letters(char box_line[VAR_NUM][STR_BUFFER_SIZE], int var_strlen_max[], int all_bold) 139 | { 140 | int i, j; 141 | 142 | print_ch(VL); 143 | for (i = 0; i < VAR_NUM; i++) 144 | { 145 | if (all_bold || i == 0) 146 | { 147 | printf(ANSI_COLOR_CYAN ANSI_FONT_BOLD"%s" ANSI_COLOR_RESET, box_line[i]); 148 | } 149 | else 150 | { 151 | if (i == 3) 152 | { 153 | if (strcmp(box_line[i], " online ") == 0) 154 | { 155 | printf(ANSI_COLOR_GREEN ANSI_FONT_BOLD"%s" ANSI_COLOR_RESET, box_line[i]); 156 | } 157 | else 158 | { 159 | printf(ANSI_COLOR_RED ANSI_FONT_BOLD"%s" ANSI_COLOR_RESET, box_line[i]); 160 | } 161 | } 162 | else 163 | { 164 | printf("%s", box_line[i]); 165 | } 166 | 167 | } 168 | 169 | for (j = 0; j < var_strlen_max[i] - strlen(box_line[i]); j++) 170 | { 171 | printf(" "); 172 | } 173 | print_ch(VL); 174 | } 175 | printf("\n"); 176 | } 177 | 178 | void get_varible_strlen_max(int var_strlen_max[], char box_line_list[][VAR_NUM][STR_BUFFER_SIZE], int list_count) 179 | { 180 | int i, j; 181 | int max; 182 | int str_len; 183 | 184 | for (i = 0; i < VAR_NUM; i++) 185 | { 186 | max = 0; 187 | for (j = 0; j < list_count; j++) 188 | { 189 | str_len = strlen(box_line_list[j][i]); 190 | if (str_len > max){ 191 | max = str_len; 192 | } 193 | } 194 | var_strlen_max[i] = max; 195 | } 196 | } 197 | 198 | 199 | 200 | void print_ch(char* ch) 201 | { 202 | printf(ANSI_FONT_BOLD ANSI_COLOR_GRAY "%s" ANSI_COLOR_RESET, ch); 203 | } 204 | 205 | void print_top_line(int var_strlen_max[]) 206 | { 207 | int i, j; 208 | print_ch(LT); 209 | for (i = 0; i < VAR_NUM; i++) 210 | { 211 | for (j = 0; j < var_strlen_max[i]; j++) 212 | { 213 | print_ch(HL); 214 | } 215 | if (i == VAR_NUM -1) 216 | { 217 | print_ch(RT); 218 | } 219 | else 220 | { 221 | print_ch(TC); 222 | } 223 | } 224 | printf("\n"); 225 | } 226 | 227 | void print_bot_line(int var_strlen_max[]) 228 | { 229 | int i, j; 230 | print_ch(LB); 231 | for (i = 0; i < VAR_NUM; i++) 232 | { 233 | for (j = 0; j < var_strlen_max[i]; j++) 234 | { 235 | print_ch(HL); 236 | } 237 | 238 | if (i == VAR_NUM -1){ 239 | print_ch(RB); 240 | } 241 | else 242 | { 243 | print_ch(BC); 244 | } 245 | } 246 | printf("\n"); 247 | } 248 | 249 | void print_mid_line(int var_strlen_max[]) 250 | { 251 | int i, j; 252 | print_ch(LC); 253 | for (i = 0; i < VAR_NUM; i++) 254 | { 255 | for (j = 0; j < var_strlen_max[i]; j++) 256 | { 257 | print_ch(HL); 258 | } 259 | 260 | if (i == VAR_NUM -1){ 261 | print_ch(RC); 262 | } 263 | else 264 | { 265 | print_ch(MC); 266 | } 267 | } 268 | printf("\n"); 269 | } 270 | -------------------------------------------------------------------------------- /src/box_drawing.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_BOX_DRAWING_H 26 | #define KBPM_BOX_DRAWING_H 27 | #include "process.h" 28 | 29 | #define RB "\e(0\x6a\e(B" /* 188 Right Bottom corner */ 30 | #define RT "\e(0\x6b\e(B" /* 187 Right Top corner */ 31 | #define LT "\e(0\x6c\e(B" /* 201 Left Top cornet */ 32 | #define LB "\e(0\x6d\e(B" /* 200 Left Bottom corner */ 33 | #define MC "\e(0\x6e\e(B" /* 206 Midle Cross */ 34 | #define HL "\e(0\x71\e(B" /* 205 Horizontal Line */ 35 | #define LC "\e(0\x74\e(B" /* 204 Left Cross */ 36 | #define RC "\e(0\x75\e(B" /* 185 Right Cross */ 37 | #define BC "\e(0\x76\e(B" /* 202 Bottom Cross */ 38 | #define TC "\e(0\x77\e(B" /* 203 Top Cross */ 39 | #define VL "\e(0\x78\e(B" /* 186 Vertical Line */ 40 | 41 | #ifndef LIST_SIZE 42 | #define LIST_SIZE 256 43 | #endif /*LIST_SIZE*/ 44 | 45 | #define VAR_NUM 7 46 | 47 | #ifndef STR_BUFFER_SIZE 48 | #define STR_BUFFER_SIZE 512 49 | #endif /*STR_BUFFER_SIZE*/ 50 | /* 51 | type struct { 52 | app_name char[STR_BUFFER_SIZE], 53 | id char[STR_BUFFER_SIZE], 54 | pid char[STR_BUFFER_SIZE], 55 | status char[STR_BUFFER_SIZE], 56 | restart char[STR_BUFFER_SIZE], 57 | uptime char[STR_BUFFER_SIZE], 58 | memory char[STR_BUFFER_SIZE] 59 | } box_line_s; 60 | */ 61 | 62 | /*print the process status box according to process list*/ 63 | extern void print_process_list_box(process_s process_list[], int list_count); 64 | 65 | #endif /* KBPM_BOX_DRAWING_H */ 66 | -------------------------------------------------------------------------------- /src/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "client.h" 34 | #include "utils.h" 35 | #include "kbpm.h" 36 | #include "ansi_escape.h" 37 | 38 | /*show that KB_PM server is not starting*/ 39 | #define PRINT_NOT_ONLINE fprintf(stderr, \ 40 | APP_NAME ": Server is not online.\n" \ 41 | ANSI_FONT_ITALIC \ 42 | " Use `kbpm service start` to start the KB_PM service\n" \ 43 | ANSI_COLOR_RESET) 44 | #define PRINT_SERVER_ERR fprintf(stderr, APP_NAME ": Server error occur.\n") 45 | 46 | /*send signal to server and get the result back*/ 47 | void communicate_with_server(const char* sig, int* res, char* buffer); 48 | 49 | /*send two signals to server and get the result back*/ 50 | void communicate_two_signals_with_server(char* sig1, char* sig2, int* res, char* buffer); 51 | 52 | void start_process(const char *app_name_or_cmd) 53 | { 54 | int res, size; 55 | char sig[BUFFER_SIZE]; 56 | char buffer[BUFFER_SIZE]; 57 | char dst[LIST_SIZE][STR_BUFFER_SIZE]; 58 | char cmd_copy[STR_BUFFER_SIZE]; 59 | process_s process; 60 | 61 | /*check if the server is online.*/ 62 | ping_server(&res); 63 | if(res != 0) 64 | { 65 | PRINT_NOT_ONLINE; 66 | exit(EXIT_FAILURE); 67 | } 68 | 69 | /*if cmd not exist, send app_name or id */ 70 | if(access(app_name_or_cmd, F_OK) == -1) 71 | { 72 | communicate_two_signals_with_server("start", (char *)app_name_or_cmd, &res, buffer); 73 | if(res != 0) 74 | { 75 | PRINT_SERVER_ERR; 76 | exit(EXIT_FAILURE); 77 | } 78 | printf(APP_NAME ": %s\n", buffer); 79 | show_status(); 80 | exit(EXIT_SUCCESS); 81 | } 82 | 83 | /*check if cmd can be executed.*/ 84 | if(access(app_name_or_cmd, X_OK) == -1){ 85 | fprintf(stderr, APP_NAME ": %s cannot be executed.\n", app_name_or_cmd); 86 | exit(EXIT_FAILURE); 87 | } 88 | 89 | /*get the app_name due to cmd. 90 | * For example, if the cmd is '/home/nladuo/a.out', 91 | * the app_name would be 'a.out'. 92 | */ 93 | strcpy(cmd_copy, app_name_or_cmd); 94 | size = str_split(dst, (char *)cmd_copy, "/"); 95 | 96 | /*set process values.*/ 97 | strcpy(process.app_name, dst[size - 1]); 98 | getcwd(process.dir, STR_BUFFER_SIZE); 99 | strcpy(process.cmd, app_name_or_cmd); 100 | process.is_running = 1; 101 | 102 | /*get json string of process.*/ 103 | create_process_json_str(&process, sig); 104 | //printf("sig:\n%s\n", sig); 105 | 106 | communicate_two_signals_with_server("start", sig, &res, buffer); 107 | if(res != 0) 108 | { 109 | PRINT_SERVER_ERR; 110 | exit(EXIT_FAILURE); 111 | } 112 | printf(APP_NAME ": %s\n", buffer); 113 | show_status(); 114 | exit(EXIT_SUCCESS); 115 | } 116 | 117 | void restart_process(const char *app_name) 118 | { 119 | int res; 120 | char buffer[BUFFER_SIZE]; 121 | /*check if the server is online.*/ 122 | ping_server(&res); 123 | if(res != 0) 124 | { 125 | PRINT_NOT_ONLINE; 126 | exit(EXIT_FAILURE); 127 | } 128 | 129 | communicate_two_signals_with_server("restart", (char *)app_name, &res, buffer); 130 | if(res != 0) 131 | { 132 | PRINT_SERVER_ERR; 133 | exit(EXIT_FAILURE); 134 | } 135 | printf(APP_NAME ": %s\n", buffer); 136 | show_status(); 137 | exit(EXIT_SUCCESS); 138 | } 139 | 140 | void stop_process(const char *app_name) 141 | { 142 | int res; 143 | char buffer[BUFFER_SIZE]; 144 | /*check if the server is online.*/ 145 | ping_server(&res); 146 | if(res != 0) 147 | { 148 | PRINT_NOT_ONLINE; 149 | exit(EXIT_FAILURE); 150 | } 151 | 152 | communicate_two_signals_with_server("stop", (char *)app_name, &res, buffer); 153 | if(res != 0) 154 | { 155 | PRINT_SERVER_ERR; 156 | exit(EXIT_FAILURE); 157 | } 158 | printf(APP_NAME ": %s\n", buffer); 159 | show_status(); 160 | exit(EXIT_SUCCESS); 161 | } 162 | 163 | void remove_process(const char* app_name) 164 | { 165 | int res; 166 | char buffer[BUFFER_SIZE]; 167 | /*check if the server is online.*/ 168 | ping_server(&res); 169 | if(res != 0) 170 | { 171 | PRINT_NOT_ONLINE; 172 | exit(EXIT_FAILURE); 173 | } 174 | 175 | communicate_two_signals_with_server("remove", (char *)app_name, &res, buffer); 176 | if(res != 0) 177 | { 178 | PRINT_SERVER_ERR; 179 | exit(EXIT_FAILURE); 180 | } 181 | printf(APP_NAME ": %s\n", buffer); 182 | show_status(); 183 | exit(EXIT_SUCCESS); 184 | } 185 | 186 | void start_all(void) 187 | { 188 | int res; 189 | char buffer[BUFFER_SIZE]; 190 | /*check if the server is online.*/ 191 | ping_server(&res); 192 | if(res != 0) 193 | { 194 | PRINT_NOT_ONLINE; 195 | exit(EXIT_FAILURE); 196 | } 197 | 198 | communicate_with_server("startall", &res, buffer); 199 | if(res != 0) 200 | { 201 | PRINT_SERVER_ERR; 202 | exit(EXIT_FAILURE); 203 | } 204 | printf(APP_NAME ": %s\n", buffer); 205 | show_status(); 206 | exit(EXIT_SUCCESS); 207 | } 208 | 209 | void stop_all(void) 210 | { 211 | int res; 212 | char buffer[BUFFER_SIZE]; 213 | /*check if the server is online.*/ 214 | ping_server(&res); 215 | if(res != 0) 216 | { 217 | PRINT_NOT_ONLINE; 218 | exit(EXIT_FAILURE); 219 | } 220 | 221 | communicate_with_server("stopall", &res, buffer); 222 | if(res != 0) 223 | { 224 | PRINT_SERVER_ERR; 225 | exit(EXIT_FAILURE); 226 | } 227 | printf(APP_NAME ": %s\n", buffer); 228 | show_status(); 229 | exit(EXIT_SUCCESS); 230 | } 231 | 232 | void show_status(void) 233 | { 234 | int i; 235 | int res, count; 236 | char buffer[BUFFER_SIZE]; 237 | process_s process_list[LIST_SIZE]; 238 | process_s *process; 239 | /*check if the server is online.*/ 240 | ping_server(&res); 241 | if(res != 0) 242 | { 243 | PRINT_NOT_ONLINE; 244 | exit(EXIT_FAILURE); 245 | } 246 | 247 | communicate_with_server("status", &res, buffer); 248 | if(res != 0) 249 | { 250 | PRINT_SERVER_ERR; 251 | exit(EXIT_FAILURE); 252 | } 253 | 254 | count = parse_process_list_with_status(process_list, buffer); 255 | print_process_list_box(process_list, count); 256 | exit(EXIT_SUCCESS); 257 | } 258 | 259 | void ping_server(int *result) 260 | { 261 | int res; 262 | char buffer[BUFFER_SIZE]; 263 | char sig[BUFFER_SIZE] = "ping"; 264 | communicate_with_server(sig, &res, buffer); 265 | if(res == -1) 266 | { 267 | *result = -1; 268 | return; 269 | } 270 | 271 | if(strcmp(buffer, "pong") != 0) 272 | { 273 | *result = -1; 274 | return; 275 | } 276 | 277 | *result = 0; 278 | } 279 | 280 | /*send signal to server and get the result back*/ 281 | void communicate_with_server(const char* sig, int* res, char* buffer) 282 | { 283 | int sockfd,len; 284 | struct sockaddr_un addr; 285 | 286 | sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 287 | 288 | addr.sun_family = AF_UNIX; 289 | strcpy(addr.sun_path, LOCAL_SOCKET_NAME); 290 | len = sizeof(addr); 291 | 292 | *res = connect(sockfd, (struct sockaddr*)&addr, len); 293 | if(*res == -1) 294 | { 295 | /*perror("socket err:");*/ 296 | return; 297 | } 298 | 299 | write(sockfd, sig, strlen(sig)); 300 | read(sockfd, buffer, BUFFER_SIZE); 301 | /*printf("read form server:%s\n", buffer);*/ 302 | close(sockfd); 303 | *res = 0; 304 | } 305 | 306 | /*send two signals to server and get the result back*/ 307 | void communicate_two_signals_with_server(char* sig1, char* sig2, int* res, char* buffer) 308 | { 309 | int sockfd,len; 310 | struct sockaddr_un addr; 311 | 312 | sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 313 | 314 | addr.sun_family = AF_UNIX; 315 | strcpy(addr.sun_path, LOCAL_SOCKET_NAME); 316 | len = sizeof(addr); 317 | 318 | *res = connect(sockfd, (struct sockaddr*)&addr, len); 319 | if(*res == -1){ 320 | /*perror("socket err:");*/ 321 | return; 322 | } 323 | write(sockfd, sig1, strlen(sig1)); 324 | read(sockfd, buffer, BUFFER_SIZE); 325 | if(strcmp(buffer, "pong") != 0){ 326 | *res = -1; 327 | return; 328 | } 329 | /*printf("sig1:read form server:%s\n", buffer);*/ 330 | 331 | write(sockfd, sig2, strlen(sig2)); 332 | read(sockfd, buffer, BUFFER_SIZE); 333 | /*printf("sig2:read form server:%s\n", buffer);*/ 334 | close(sockfd); 335 | *res = 0; 336 | } 337 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2016 Kalen Blue 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | 26 | #ifndef KBPM_CLIENT_H 27 | #define KBPM_CLIENT_H 28 | 29 | #include "process.h" 30 | 31 | #ifndef LOCAL_SOCKET_NAME 32 | #define LOCAL_SOCKET_NAME "/tmp/kbpm.sock" 33 | #endif /*LOCAL_SOCKET_NAME*/ 34 | 35 | #ifndef BUFFER_SIZE 36 | #define BUFFER_SIZE 40 * 1024 37 | #endif /*BUFFER_SIZE*/ 38 | 39 | /*call request to start a process by app_name or cmd.*/ 40 | extern void start_process(const char *app_name_or_cmd); 41 | 42 | /*call request to restart a process by app_name.*/ 43 | extern void restart_process(const char *app_name); 44 | 45 | /*call request to stop a process by app name.*/ 46 | extern void stop_process(const char *app_name); 47 | 48 | extern void remove_process(const char* app_name); 49 | 50 | extern void start_all(void); 51 | 52 | extern void stop_all(void); 53 | 54 | extern void show_status(void); 55 | 56 | extern void ping_server(int* res); 57 | 58 | #endif /*KBPM_CLIENT_H*/ 59 | -------------------------------------------------------------------------------- /src/kbpm.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include "server.h" 29 | #include "client.h" 30 | #include "kbpm.h" 31 | 32 | /*print the usage of kb_pm*/ 33 | int print_usage(void); 34 | 35 | int main(int argc, char** argv) 36 | { 37 | int res; 38 | if(argc == 1) 39 | { 40 | print_usage(); 41 | exit(EXIT_SUCCESS); 42 | } 43 | 44 | if(argc == 2) 45 | { 46 | /*start all process.*/ 47 | if(strcmp(argv[1], "startall") == 0) 48 | { 49 | start_all(); 50 | } 51 | 52 | /*stop all process.*/ 53 | if(strcmp(argv[1], "stopall") == 0) 54 | { 55 | stop_all(); 56 | } 57 | 58 | /*show the processes status*/ 59 | if(strcmp(argv[1], "status") == 0) 60 | { 61 | show_status(); 62 | } 63 | 64 | /*show the version of KB_PM*/ 65 | if(strcmp(argv[1], "version") == 0) 66 | { 67 | printf(APP_NAME ": %s\n", APP_VERSION); 68 | exit(EXIT_SUCCESS); 69 | } 70 | fprintf(stderr, APP_NAME ": Arguments error, see usage.\n"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | if(argc == 3) 75 | { 76 | /*service start.*/ 77 | if(strcmp(argv[1], "service") == 0) 78 | { 79 | if (strcmp(argv[2], "start") == 0) 80 | { 81 | ping_server(&res); 82 | if (res != 0) 83 | { 84 | printf(APP_NAME ": Starting the server.\n" 85 | ANSI_FONT_ITALIC " Use `kbpm start ` to make an app run forever\n" ANSI_COLOR_RESET); 86 | service_start(); 87 | } 88 | else 89 | { 90 | printf(APP_NAME ": Service have been already started.\n" 91 | ANSI_FONT_ITALIC " Use `kbpm status` to get the status of program(s)\n" ANSI_COLOR_RESET); 92 | exit(EXIT_SUCCESS); 93 | } 94 | } 95 | 96 | if (strcmp(argv[2], "clean") == 0) 97 | { 98 | service_clean(); 99 | } 100 | } 101 | 102 | /*start a process.*/ 103 | if(strcmp(argv[1], "start") == 0) 104 | { 105 | start_process(argv[2]); 106 | } 107 | 108 | /*restart a process.*/ 109 | if(strcmp(argv[1], "restart") == 0) 110 | { 111 | restart_process(argv[2]); 112 | } 113 | 114 | /*stop a process.*/ 115 | if(strcmp(argv[1], "stop") == 0) 116 | { 117 | stop_process(argv[2]); 118 | } 119 | 120 | /*remove a process.*/ 121 | if(strcmp(argv[1], "remove") == 0) 122 | { 123 | remove_process(argv[2]); 124 | } 125 | 126 | fprintf(stderr, APP_NAME ": Arguments error, see usage.\n"); 127 | exit(EXIT_FAILURE); 128 | } 129 | 130 | fprintf(stderr, APP_NAME ": Too many arguments, see usage.\n"); 131 | exit(EXIT_FAILURE); 132 | } 133 | 134 | int print_usage(void) 135 | { 136 | return printf( 137 | APP_NAME ": a CLI tool (like supervisor and pm2) keep a group of program running continuously.\n" 138 | " Usage : kbpm [cmd] app\n" 139 | " service start start the KB_PM service\n" 140 | " service clean resolve the KB_PM service unexpectedly exit.\n" 141 | " start start the program and run forever\n" 142 | " restart restart the program\n" 143 | " stop stop the program\n" 144 | " remove remove the program form list\n" 145 | " startall start all program(s)\n" 146 | " stopall stop all the program(s)\n" 147 | " version show KB_PM version\n" 148 | ); 149 | } 150 | -------------------------------------------------------------------------------- /src/kbpm.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_MAIN_H 26 | #define KBPM_MAIN_H 27 | 28 | #include "ansi_escape.h" 29 | 30 | #define APP_NAME ANSI_COLOR_GREEN "[KB_PM]" ANSI_COLOR_RESET 31 | #define APP_VERSION "v0.1.0" 32 | 33 | #endif /*KBPM_MAIN_H*/ 34 | -------------------------------------------------------------------------------- /src/proc_status.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "utils.h" 8 | #include "proc_status.h" 9 | 10 | #define PROC_DIR "/proc/" 11 | 12 | void get_child_pids(pid_t parent_pid, pid_t pids[], int *p_pids_len) 13 | { 14 | DIR *dp; 15 | struct dirent *entry; 16 | struct stat stat_buf; 17 | pid_t pid; 18 | char buf[STR_BUFFER_SIZE]; 19 | char parent_pid_str[STR_BUFFER_SIZE]; 20 | int pids_len = 0; 21 | 22 | *p_pids_len = -1; 23 | 24 | /*transfer pid to char*/ 25 | sprintf(parent_pid_str, "%d", parent_pid); 26 | 27 | if((dp = opendir(PROC_DIR)) == NULL) 28 | { 29 | return; 30 | } 31 | chdir(PROC_DIR); 32 | while((entry = readdir(dp)) != NULL) 33 | { 34 | lstat(entry->d_name, &stat_buf); 35 | if(S_ISDIR(stat_buf.st_mode)) 36 | { 37 | if(!is_number(entry->d_name)) 38 | { 39 | continue; 40 | } 41 | pid = atoi(entry->d_name); 42 | if(pid > 0 && pid <= 32767) 43 | { 44 | parse_proc_status_val_by_tag(pid, "PPid", buf); 45 | if(strcmp(buf, ERR_RESULT) != 0 46 | && strcmp(parent_pid_str, buf) == 0) 47 | { 48 | //fprintf(stdout, "ppid:%s, pid:%s\n", buf, entry->d_name); 49 | pids[pids_len++] = pid; 50 | } 51 | } 52 | } 53 | } 54 | *p_pids_len = pids_len; 55 | } 56 | 57 | void parse_proc_status_val_by_tag(pid_t pid, char* tag , char* result_val) 58 | { 59 | FILE *fp; 60 | int flag = 0; 61 | char buf[STR_BUFFER_SIZE]; 62 | char path[STR_BUFFER_SIZE]; 63 | char *p_buf; 64 | 65 | sprintf(path, "/proc/%d/status", pid); 66 | 67 | fp = fopen(path, "r"); 68 | while(fgets(buf, STR_BUFFER_SIZE, fp) > 0) 69 | { 70 | if (strncmp(buf, tag, strlen(tag)) == 0) 71 | { 72 | /*the buf[strlen(buf)-1] should be '\n', replace it with '\0' */ 73 | buf[strlen(buf)-1] = 0; 74 | 75 | flag = 1; 76 | p_buf = buf; 77 | while(!(*p_buf >= '0' && *p_buf <= '9')) p_buf++; 78 | strcpy(result_val, p_buf); 79 | break; 80 | } 81 | } 82 | fclose(fp); 83 | if (!flag) 84 | { 85 | strcpy(result_val, ERR_RESULT); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/proc_status.h: -------------------------------------------------------------------------------- 1 | #ifndef KBPM_PROC_H 2 | #define KBPM_PROC_H 3 | 4 | #ifndef STR_BUFFER_SIZE 5 | #define STR_BUFFER_SIZE 512 6 | #endif /*STR_BUFFER_SIZE*/ 7 | 8 | #ifndef BUFFER_SIZE 9 | #define BUFFER_SIZE 40 * 1024 10 | #endif /*BUFFER_SIZE*/ 11 | 12 | #ifndef LIST_SIZE 13 | #define LIST_SIZE 256 14 | #endif /*LIST_SIZE*/ 15 | 16 | #define ERR_RESULT "error_result" 17 | 18 | /*get the child pids by a parent pid*/ 19 | void get_child_pids(pid_t parent_pid, pid_t pids[], int *p_pids_len); 20 | 21 | /*parse the /proc/(pid)/status file*/ 22 | void parse_proc_status_val_by_tag(pid_t pid, char *tag, char* result_val); 23 | 24 | #endif /*KBPM_PROC_H*/ 25 | -------------------------------------------------------------------------------- /src/process.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "process.h" 34 | #include "proc_status.h" 35 | #include "../lib/cJSON.h" 36 | 37 | int parse_process_cJSON(process_s *process, cJSON *p_json); 38 | 39 | int parse_process_cJSON_with_status(process_s *process, cJSON *p_json); 40 | 41 | void create_process_cJSON(process_s *process, cJSON* p_json); 42 | 43 | void create_process_cJSON_with_status(process_s *process, cJSON* p_json); 44 | 45 | void get_mem_by_pid(pid_t pid, char* memory); 46 | 47 | void kill_process_by_pid(pid_t pid); 48 | 49 | /*execute a process*/ 50 | void exec_process(process_s *process, int* res) 51 | { 52 | chdir(process->dir); 53 | *res = execl("/bin/bash", "bash", "-c", process->cmd, (char*)0); 54 | } 55 | 56 | /*kill a process*/ 57 | void kill_process(process_s *process, int* res) 58 | { 59 | kill_process_by_pid(process->pid); 60 | } 61 | 62 | /*recursively kill the process and it children process*/ 63 | void kill_process_by_pid(pid_t pid) 64 | { 65 | pid_t child_pids[CHILD_PROCESS_LEN]; 66 | int pids_len, i; 67 | get_child_pids(pid, child_pids, &pids_len); 68 | /*kill the parent process*/ 69 | kill(pid, SIGKILL); 70 | if(pids_len > 0) 71 | { 72 | for(i = 0; i < pids_len; i++) 73 | { 74 | kill_process_by_pid(child_pids[i]); 75 | } 76 | } 77 | } 78 | /*save the process_list count into int pointer "count"*/ 79 | void get_process_list_count(process_s process_list[], int* count) 80 | { 81 | int i; 82 | for(i = 0; i < LIST_SIZE; i++) 83 | { 84 | if(!strlen(process_list[i].app_name)) break; 85 | } 86 | *count = i; 87 | } 88 | 89 | /* 90 | * return process_list count. 91 | */ 92 | int parse_process_list_from_path(const char* path, process_s process_list[]) 93 | { 94 | int in_d, res; 95 | char buffer[BUFFER_SIZE]; 96 | in_d = open(path, O_RDONLY); 97 | if(read(in_d, buffer, BUFFER_SIZE) <=0) 98 | { 99 | return -1; 100 | } 101 | res = parse_process_list(process_list, buffer); 102 | 103 | return res; 104 | } 105 | 106 | int del_process_by_app_name(process_s process_list[], char* app_name) 107 | { 108 | int i,index,count; 109 | index = -1; 110 | /* find the index of app_name */ 111 | get_process_list_count(process_list, &count); 112 | for(i = 0; i < count; i++) 113 | { 114 | if(!strcmp(process_list[i].app_name, app_name)) 115 | { 116 | index = i; 117 | } 118 | } 119 | if(index == -1) 120 | { 121 | return -1; 122 | } 123 | /* remove the process */ 124 | for(i = index; i < count; i++) 125 | { 126 | process_list[i + 1].id--; 127 | process_list[i] = process_list[i + 1]; 128 | } 129 | 130 | return 0; 131 | } 132 | 133 | void save_process_list(const char* path, process_s process_list[]) 134 | { 135 | FILE *fp; 136 | int i,count; 137 | char buffer[BUFFER_SIZE]; 138 | process_s* process; 139 | fp = fopen(path, "w"); 140 | 141 | /*save the processes*/ 142 | get_process_list_count(process_list, &count); 143 | create_process_list_json_str(process_list, count, buffer); 144 | fprintf(fp, "%s", buffer); 145 | fflush(fp); 146 | fclose(fp); 147 | } 148 | 149 | 150 | 151 | int parse_process_list(process_s process_list[], char* json_str) 152 | { 153 | cJSON *p_json_array, *p_json; 154 | process_s *process; 155 | int size, res, i; 156 | if(NULL == json_str) 157 | { 158 | return -1; 159 | } 160 | p_json_array = cJSON_Parse(json_str); 161 | if(NULL == p_json_array) 162 | { 163 | return -1; 164 | } 165 | size = cJSON_GetArraySize(p_json_array); 166 | for(i = 0; i < size; i++) 167 | { 168 | p_json = cJSON_GetArrayItem(p_json_array, i); 169 | process = &process_list[i]; 170 | res = parse_process_cJSON(process, p_json); 171 | 172 | /*if parse err occur, free memory and return*/ 173 | if(res == -1) 174 | { 175 | cJSON_Delete(p_json_array); 176 | return -1; 177 | } 178 | } 179 | cJSON_Delete(p_json_array); 180 | return size; 181 | } 182 | 183 | int parse_process_list_with_status(process_s process_list[], char* json_str) 184 | { 185 | cJSON *p_json_array, *p_json; 186 | process_s *process; 187 | int size, res, i; 188 | if(NULL == json_str) 189 | { 190 | return -1; 191 | } 192 | p_json_array = cJSON_Parse(json_str); 193 | if(NULL == p_json_array) 194 | { 195 | return -1; 196 | } 197 | size = cJSON_GetArraySize(p_json_array); 198 | for(i = 0; i < size; i++) 199 | { 200 | p_json = cJSON_GetArrayItem(p_json_array, i); 201 | process = &process_list[i]; 202 | res = parse_process_cJSON_with_status(process, p_json); 203 | 204 | /*if parse err occur, free memory and return*/ 205 | if(res == -1) 206 | { 207 | cJSON_Delete(p_json_array); 208 | return -1; 209 | } 210 | } 211 | cJSON_Delete(p_json_array); 212 | return size; 213 | } 214 | 215 | int parse_process(process_s *process, char* json_str) 216 | { 217 | cJSON* p_json; 218 | int res; 219 | if(NULL == json_str) 220 | { 221 | return -1; 222 | } 223 | p_json = cJSON_Parse(json_str); 224 | 225 | if(NULL == p_json) 226 | { 227 | return -1; 228 | } 229 | res = parse_process_cJSON(process, p_json); 230 | cJSON_Delete(p_json); 231 | return res; 232 | } 233 | 234 | int parse_process_cJSON(process_s *process, cJSON *p_json) 235 | { 236 | 237 | /*get app_name*/ 238 | cJSON* p_json_item = cJSON_GetObjectItem(p_json, "app_name"); 239 | if(NULL == p_json_item) 240 | { 241 | return -1; 242 | } 243 | strcpy(process->app_name, p_json_item->valuestring); 244 | 245 | /*get cmd*/ 246 | p_json_item = cJSON_GetObjectItem(p_json, "cmd"); 247 | if(NULL == p_json_item) 248 | { 249 | return -1; 250 | } 251 | strcpy(process->cmd, p_json_item->valuestring); 252 | 253 | /*get dir*/ 254 | p_json_item = cJSON_GetObjectItem(p_json, "dir"); 255 | if(NULL == p_json_item) 256 | { 257 | return -1; 258 | } 259 | strcpy(process->dir, p_json_item->valuestring); 260 | 261 | /*get is_running*/ 262 | p_json_item = cJSON_GetObjectItem(p_json, "is_running"); 263 | if(NULL == p_json_item) 264 | { 265 | return -1; 266 | } 267 | process->is_running = p_json_item->valueint; 268 | } 269 | 270 | int parse_process_cJSON_with_status(process_s *process, cJSON *p_json) 271 | { 272 | 273 | /*get app_name*/ 274 | cJSON* p_json_item = cJSON_GetObjectItem(p_json, "app_name"); 275 | if(NULL == p_json_item) 276 | { 277 | return -1; 278 | } 279 | strcpy(process->app_name, p_json_item->valuestring); 280 | 281 | /*get id*/ 282 | p_json_item = cJSON_GetObjectItem(p_json, "id"); 283 | if(NULL == p_json_item) 284 | { 285 | return -1; 286 | } 287 | process->id = p_json_item->valueint; 288 | 289 | /*get pid*/ 290 | p_json_item = cJSON_GetObjectItem(p_json, "pid"); 291 | if(NULL == p_json_item) 292 | { 293 | return -1; 294 | } 295 | process->pid = p_json_item->valueint; 296 | 297 | /*get is_running*/ 298 | p_json_item = cJSON_GetObjectItem(p_json, "is_running"); 299 | if(NULL == p_json_item) 300 | { 301 | return -1; 302 | } 303 | process->is_running = p_json_item->valueint; 304 | 305 | /*get restart_times*/ 306 | p_json_item = cJSON_GetObjectItem(p_json, "restart_times"); 307 | if(NULL == p_json_item) 308 | { 309 | return -1; 310 | } 311 | process->restart_times = p_json_item->valueint; 312 | 313 | /*get start_time*/ 314 | p_json_item = cJSON_GetObjectItem(p_json, "start_time"); 315 | if(NULL == p_json_item) 316 | { 317 | return -1; 318 | } 319 | process->start_time = p_json_item->valueint; 320 | 321 | /*get memory*/ 322 | p_json_item = cJSON_GetObjectItem(p_json, "memory"); 323 | if(NULL == p_json_item) 324 | { 325 | return -1; 326 | } 327 | strcpy(process->memory, p_json_item->valuestring); 328 | } 329 | 330 | int create_process_list_json_str(process_s process_list[LIST_SIZE], int list_count, char* json_str) 331 | { 332 | cJSON *p_json_array, *p_json; 333 | process_s *process; 334 | char* str; 335 | int i; 336 | p_json_array = cJSON_CreateArray(); 337 | if(NULL == p_json_array) 338 | { 339 | return -1; 340 | } 341 | for(i = 0; i < list_count; i++) 342 | { 343 | process = &process_list[i]; 344 | p_json = cJSON_CreateObject(); 345 | if(NULL == p_json){ 346 | cJSON_Delete(p_json_array); 347 | return -1; 348 | } 349 | create_process_cJSON(process, p_json); 350 | cJSON_AddItemToArray(p_json_array, p_json); 351 | } 352 | 353 | str = cJSON_Print(p_json_array); 354 | if(NULL == str){ 355 | cJSON_Delete(p_json_array); 356 | return -1; 357 | } 358 | strcpy(json_str, str); 359 | cJSON_Delete(p_json_array); 360 | return 0; 361 | } 362 | 363 | int create_process_list_json_str_with_status(process_s process_list[], int list_count, char* json_str) 364 | { 365 | cJSON *p_json_array, *p_json; 366 | process_s *process; 367 | char* str; 368 | int i; 369 | p_json_array = cJSON_CreateArray(); 370 | if(NULL == p_json_array) 371 | { 372 | return -1; 373 | } 374 | for(i = 0; i < list_count; i++) 375 | { 376 | process = &process_list[i]; 377 | p_json = cJSON_CreateObject(); 378 | if(NULL == p_json) 379 | { 380 | cJSON_Delete(p_json_array); 381 | return -1; 382 | } 383 | create_process_cJSON_with_status(process, p_json); 384 | cJSON_AddItemToArray(p_json_array, p_json); 385 | } 386 | 387 | str = cJSON_Print(p_json_array); 388 | if(NULL == str) 389 | { 390 | cJSON_Delete(p_json_array); 391 | return -1; 392 | } 393 | strcpy(json_str, str); 394 | cJSON_Delete(p_json_array); 395 | return 0; 396 | } 397 | 398 | int create_process_json_str(process_s *process, char* json_str) 399 | { 400 | cJSON* p_json; 401 | char* str = NULL; 402 | p_json = cJSON_CreateObject(); 403 | if(NULL == p_json) 404 | { 405 | return -1; 406 | } 407 | create_process_cJSON(process, p_json); 408 | 409 | str = cJSON_Print(p_json); 410 | 411 | if(NULL == str) 412 | { 413 | cJSON_Delete(p_json); 414 | return -1; 415 | } 416 | 417 | strcpy(json_str, str); 418 | cJSON_Delete(p_json); 419 | return 0; 420 | } 421 | 422 | void create_process_cJSON(process_s *process, cJSON* p_json) 423 | { 424 | cJSON_AddStringToObject(p_json, "app_name", process->app_name); 425 | cJSON_AddStringToObject(p_json, "cmd", process->cmd); 426 | cJSON_AddStringToObject(p_json, "dir", process->dir); 427 | cJSON_AddNumberToObject(p_json, "is_running", process->is_running); 428 | } 429 | 430 | void create_process_cJSON_with_status(process_s *process, cJSON* p_json) 431 | { 432 | cJSON_AddStringToObject(p_json, "app_name", process->app_name); 433 | cJSON_AddNumberToObject(p_json, "id", process->id); 434 | cJSON_AddNumberToObject(p_json, "pid", process->pid); 435 | cJSON_AddNumberToObject(p_json, "restart_times", process->restart_times); 436 | cJSON_AddNumberToObject(p_json, "start_time", process->start_time); 437 | cJSON_AddNumberToObject(p_json, "is_running", process->is_running); 438 | /*get process memory*/ 439 | if (process->is_running) 440 | { 441 | get_mem_by_pid(process->pid, process->memory); 442 | cJSON_AddStringToObject(p_json, "memory", process->memory); 443 | } 444 | else 445 | { 446 | cJSON_AddStringToObject(p_json, "memory", "0 B"); 447 | } 448 | 449 | } 450 | 451 | void get_mem_by_pid(pid_t pid, char* memory) 452 | { 453 | parse_proc_status_val_by_tag(pid, "VmHWM", memory); 454 | if (strcmp(memory, ERR_RESULT) == 0) 455 | { 456 | strcpy(memory, "cannot get memory"); 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /src/process.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_PROCESS_H 26 | #define KBPM_PROCESS_H 27 | 28 | #ifndef STR_BUFFER_SIZE 29 | #define STR_BUFFER_SIZE 512 30 | #endif /*STR_BUFFER_SIZE*/ 31 | 32 | #ifndef BUFFER_SIZE 33 | #define BUFFER_SIZE 40 * 1024 34 | #endif /*BUFFER_SIZE*/ 35 | 36 | #ifndef LIST_SIZE 37 | #define LIST_SIZE 256 38 | #endif /*LIST_SIZE*/ 39 | 40 | #define CHILD_PROCESS_LEN 4096 41 | 42 | /*define the process struct.*/ 43 | typedef struct{ 44 | char app_name[STR_BUFFER_SIZE]; 45 | char cmd[STR_BUFFER_SIZE]; 46 | char dir[STR_BUFFER_SIZE]; 47 | pid_t pid; 48 | int is_running; /* 0 for false, 1 for true.*/ 49 | int id; 50 | char memory[STR_BUFFER_SIZE]; 51 | int restart_times; 52 | time_t start_time; 53 | 54 | }process_s; 55 | 56 | /* exec a process by cmd in bash shell, and get the result by pointer.*/ 57 | extern void exec_process(process_s* process, int* res); 58 | 59 | /* kill a process by process pid, and get the result by pointer.*/ 60 | extern void kill_process(process_s* process, int* res); 61 | 62 | /*read json_str from path, parse it to process_list, and return the size of process_list.*/ 63 | extern int parse_process_list_from_path(const char *path, process_s process_list[]); 64 | 65 | /*parse the json_str to process_list, and return the size of process_list.*/ 66 | extern int parse_process_list(process_s process_list[], char* json_str); 67 | 68 | extern int parse_process_list_with_status(process_s process_list[], char* json_str); 69 | 70 | /*parse the json_str to process.*/ 71 | extern int parse_process(process_s *process, char* json_str); 72 | 73 | /*get the json_str from process_list.*/ 74 | extern int create_process_list_json_str(process_s process_list[], int list_count, char* json_str); 75 | 76 | extern int create_process_list_json_str_with_status(process_s process_list[], int list_count, char* json_str); 77 | 78 | /*get the json_str from process.*/ 79 | extern int create_process_json_str(process_s *process, char* json_str); 80 | 81 | /*how many known process used KB_PM to manage.*/ 82 | extern void get_process_list_count(process_s process_list[], int* count); 83 | 84 | /* delete a process in process list, if find the app, return 1, if did not, return 0. */ 85 | extern int del_process_by_app_name(process_s process_list[], char* app_name); 86 | 87 | /*save the process in file.*/ 88 | extern void save_process_list(const char* path, process_s process_list[]); 89 | 90 | #endif /*KBPM_PROCESS_H*/ 91 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Kalen Blue 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "process.h" 37 | #include "server.h" 38 | #include "kbpm.h" 39 | #include "utils.h" 40 | 41 | /*daemonize the process*/ 42 | void init_daemon(void); 43 | 44 | /*ignore some signals except SIGKILL, SIGSTOP*/ 45 | void ignore_signals(void); 46 | 47 | /*handle the client request*/ 48 | void *server_socket_handle_thread_func(void *); 49 | 50 | /*if the "~/.kbpm/process.config" does not exsit, create it.*/ 51 | void create_config_file(); 52 | 53 | /*send 'pong' to client and read the request data of client into buffer.*/ 54 | void pong_and_receive_data(int client_sockfd, char* buffer); 55 | 56 | /*get the "~/.kbpm/process.config" path*/ 57 | void get_config_path(char* config_path); 58 | 59 | /*get the "~/.kbpm" path*/ 60 | void get_config_dir(char* config_dir); 61 | 62 | /*get the process id by checkout buffer is number and in the range of processlist*/ 63 | int get_process_id(char *buffer); 64 | 65 | /*parse the process out and return the type(TYPE_APP_NAME, TYPE_ID, TYPE_CMD, TYPE_ERR)*/ 66 | int parse_process_and_process_type(char *buffer, process_s *process); 67 | 68 | /*start a process and get the reponse to client*/ 69 | void server_start_process_and_get_response(char *buffer, char* response); 70 | 71 | /*stop a process and get the reponse to client*/ 72 | void server_stop_process_and_get_response(char *buffer, char* response); 73 | 74 | /*restart a process and get the reponse to client*/ 75 | void server_restart_process_and_get_response(char *buffer, char* response); 76 | 77 | /*remove a process and get the reponse to client*/ 78 | void server_remove_process_and_get_response(char *buffer, char* response); 79 | 80 | /*start a process and add to the tail of process_list*/ 81 | void server_start_process_with_cmd(process_s *process, char* response); 82 | 83 | /*start a process in process list*/ 84 | void server_start_process(process_s *process, int with_log); 85 | 86 | /*stop a process in process list*/ 87 | void server_stop_process(process_s *process); 88 | 89 | /*restart a process in process list*/ 90 | void server_restart_process(process_s *process); 91 | 92 | /*remove a process in process list*/ 93 | void server_remove_process(process_s *process, char* app_name); 94 | 95 | /*get process list info and status.*/ 96 | void server_get_processes_status(char* buffer); 97 | 98 | /*start all processes and listening to them exit.*/ 99 | void server_start_all_process(void); 100 | 101 | /*stop all processes that server listening.*/ 102 | void server_stop_all_process(void); 103 | 104 | /*get error response by buffer*/ 105 | void get_error_reponse(char *buffer, char *reponse); 106 | 107 | /*return the position in process list*/ 108 | int get_pos_in_process_list(process_s *process); 109 | 110 | /*return the process_list status to client.*/ 111 | void get_process_list_status(char *response); 112 | 113 | /*get the quantity of running process in process_list.*/ 114 | int get_running_process_count(); 115 | 116 | /*need mutex when access process_list*/ 117 | pthread_mutex_t process_list_mutex; 118 | 119 | /*the listening process list.*/ 120 | process_s process_list[LIST_SIZE]; 121 | 122 | /*the quantity of process in process_list.*/ 123 | int process_count; 124 | /* 125 | * start the KB_PM service, and run in daemon. 126 | */ 127 | void service_start(void) 128 | { 129 | int i, res; 130 | 131 | char *home_dir; 132 | pid_t pid; 133 | process_s *process; 134 | pthread_t socket_server_thread; 135 | char config_path[STR_BUFFER_SIZE]; 136 | time_t time_now; 137 | int restart_times; 138 | 139 | /*if first run the program, the config file will be not exist, create it.*/ 140 | create_config_file(); 141 | 142 | /*get process_list when service start up.*/ 143 | get_config_path(config_path); 144 | process_count = parse_process_list_from_path(config_path, process_list); 145 | if (process_count == -1)/*if process_count is -1, there are not jspn file*/ 146 | { 147 | process_count = 0; 148 | } 149 | 150 | /*daemonize the process*/ 151 | init_daemon(); 152 | 153 | /*init mutex*/ 154 | res = pthread_mutex_init(&process_list_mutex, NULL); 155 | if(res != 0) 156 | { 157 | syslog(LOG_ERR, "Mutex initialization failed, error: %s", strerror(errno)); 158 | syslog(LOG_ERR, "server unexpected exit."); 159 | exit(EXIT_FAILURE); 160 | } 161 | 162 | /*create thread to handle client request*/ 163 | res = pthread_create(&socket_server_thread, NULL, server_socket_handle_thread_func, NULL); 164 | if(res != 0) 165 | { 166 | syslog(LOG_ERR, "Thread creating failed, error: %s", strerror(errno)); 167 | syslog(LOG_ERR, "server unexpected exit."); 168 | exit(EXIT_FAILURE); 169 | } 170 | 171 | /*exec the processes*/ 172 | for(i = 0; i < process_count; i++) 173 | { 174 | process = &process_list[i]; 175 | process->id = i; 176 | if(!process->is_running) 177 | { 178 | continue; 179 | } 180 | server_start_process(process, 1); 181 | } 182 | 183 | /*listening the child process exit.*/ 184 | while(1) 185 | { 186 | /*if no running process, there are no child process, 187 | so wait(NULL) will return -1.*/ 188 | if(!process_count || !get_running_process_count()) 189 | { 190 | /*sleep 100 ms*/ 191 | usleep(100 * 1000); 192 | } 193 | pid = wait(NULL); 194 | pthread_mutex_lock(&process_list_mutex); 195 | if (pid != -1) 196 | { 197 | syslog(LOG_INFO, "pid: %d be killed", pid); 198 | } 199 | for(i = 0; i < process_count; i++) 200 | { 201 | process = &process_list[i]; 202 | if(process->pid == pid) 203 | { 204 | if(!process->is_running) 205 | { 206 | break; 207 | } 208 | time(&time_now); 209 | /*if a program restart interval less than 1 second.*/ 210 | if (process->start_time == time_now) 211 | { 212 | process->is_running = 0; 213 | process->pid = 0; 214 | syslog(LOG_INFO, "The App(%s) stopped too quickly. KB_PM service stop it automatically.",process->app_name); 215 | break; 216 | } 217 | /*save the restart times as temp.*/ 218 | restart_times = process->restart_times; 219 | server_start_process(process, 0);/*without syslog*/ 220 | process->restart_times = restart_times + 1; 221 | syslog(LOG_INFO, "Restarting %s with pid:%d",process->app_name, process->pid); 222 | usleep(100 * 1000); 223 | } 224 | } 225 | pthread_mutex_unlock(&process_list_mutex); 226 | } 227 | } 228 | 229 | /*delete the /tmp/kbpm.sock is okay.*/ 230 | void service_clean(void) 231 | { 232 | unlink(LOCAL_SOCKET_FILE); 233 | exit(EXIT_SUCCESS); 234 | } 235 | 236 | /*a new thread that handle client msg*/ 237 | void *server_socket_handle_thread_func(void *args) 238 | { 239 | int server_sockfd, client_sockfd; 240 | struct sockaddr_un server_addr, client_addr; 241 | int server_len, client_len; 242 | char buffer[BUFFER_SIZE]; 243 | char response[BUFFER_SIZE]; 244 | char config_path[STR_BUFFER_SIZE]; 245 | 246 | get_config_path(config_path); 247 | 248 | /*delete the former sock file.*/ 249 | unlink(LOCAL_SOCKET_FILE); 250 | 251 | /*create socket.*/ 252 | server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); 253 | server_addr.sun_family = AF_UNIX; 254 | strcpy(server_addr.sun_path, LOCAL_SOCKET_FILE); 255 | server_len = sizeof(server_addr); 256 | 257 | /*bind and listen.*/ 258 | bind(server_sockfd, (struct sockaddr*)&server_addr, server_len); 259 | listen(server_sockfd, SERVER_ACCEPT_COUNT); 260 | 261 | /*loop, accept the client connection.*/ 262 | while(1) 263 | { 264 | client_len = sizeof(client_addr); 265 | client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len); 266 | pthread_mutex_lock(&process_list_mutex); 267 | 268 | read(client_sockfd, buffer, STR_BUFFER_SIZE); 269 | /*check out the command*/ 270 | if(strcmp(buffer, "startall") == 0) 271 | { 272 | /*command startall*/ 273 | server_start_all_process(); 274 | strcpy(response, "Start All Okay."); 275 | } 276 | else if(strcmp(buffer, "stopall") == 0) 277 | { 278 | /*command stopall*/ 279 | server_stop_all_process(); 280 | strcpy(response, "Stop All Okay."); 281 | } 282 | else if(strcmp(buffer, "status") == 0) 283 | { 284 | /*command status*/ 285 | get_process_list_status(response); 286 | } 287 | else if(strcmp(buffer, "start") == 0) 288 | { 289 | /*command start */ 290 | pong_and_receive_data(client_sockfd,buffer); 291 | server_start_process_and_get_response(buffer, response); 292 | } 293 | else if(strcmp(buffer, "stop") == 0) 294 | { 295 | /*command stop */ 296 | pong_and_receive_data(client_sockfd,buffer); 297 | server_stop_process_and_get_response(buffer, response); 298 | } 299 | else if(strcmp(buffer, "restart") == 0) 300 | { 301 | /*command restart */ 302 | pong_and_receive_data(client_sockfd,buffer); 303 | server_restart_process_and_get_response(buffer, response); 304 | } 305 | else if(strcmp(buffer, "remove") == 0) 306 | { 307 | /*command remove */ 308 | pong_and_receive_data(client_sockfd,buffer); 309 | server_remove_process_and_get_response(buffer, response); 310 | } 311 | else if(strcmp(buffer, "ping") == 0) 312 | { 313 | /*client ping server.*/ 314 | strcpy(response, "pong"); 315 | } 316 | else 317 | { 318 | strcpy(response, "error command"); 319 | } 320 | write(client_sockfd, response, strlen(response)); 321 | close(client_sockfd); 322 | save_process_list(config_path, process_list); 323 | memset(buffer, '\0', sizeof(buffer)); 324 | memset(response, '\0', sizeof(response)); 325 | pthread_mutex_unlock(&process_list_mutex); 326 | } 327 | } 328 | 329 | void pong_and_receive_data(int client_sockfd, char* buffer) 330 | { 331 | write(client_sockfd, "pong", strlen("pong")); 332 | memset(buffer, '\0', sizeof(buffer)); 333 | /*get the appname*/ 334 | read(client_sockfd, buffer, STR_BUFFER_SIZE); 335 | } 336 | 337 | void get_config_path(char* config_path) 338 | { 339 | /*get config path according to $HOME*/ 340 | sprintf(config_path, "%s%s", getenv("HOME"), CONFIG_PATH); 341 | } 342 | 343 | void get_config_dir(char* config_dir) 344 | { 345 | /*get config dir according to $HOME*/ 346 | sprintf(config_dir, "%s%s", getenv("HOME"), CONFIG_DIR); 347 | } 348 | 349 | int get_process_id(char *buffer) 350 | { 351 | int id; 352 | if (is_number(buffer)) 353 | { 354 | id = atoi((const char*)buffer); 355 | /*syslog(LOG_INFO, "id --> %d.",id);*/ 356 | if (id >= 0 && id < process_count) 357 | { 358 | return id; 359 | } 360 | } 361 | return -1; 362 | } 363 | 364 | int parse_process_and_process_type(char *buffer, process_s *process) 365 | { 366 | int id, i; 367 | /*check out TYPE_ID.*/ 368 | if ((id = get_process_id(buffer)) != -1) 369 | { 370 | *process = process_list[id]; 371 | return TYPE_ID; 372 | } 373 | 374 | /*check out TYPE_APP_NAME.*/ 375 | for(i = 0; i < process_count; i++) 376 | { 377 | if(strcmp(process_list[i].app_name, buffer) == 0) 378 | { 379 | *process = process_list[i]; 380 | return TYPE_APP_NAME; 381 | } 382 | } 383 | 384 | /*check out TYPE_CMD.*/ 385 | if(parse_process(process, buffer) == -1) 386 | { 387 | return TYPE_ERR; 388 | } 389 | return TYPE_CMD; 390 | } 391 | 392 | int get_pos_in_process_list(process_s *process) 393 | { 394 | int i; 395 | for (i = 0; i < process_count; i++) 396 | { 397 | if (strcmp(process_list[i].app_name, process->app_name) == 0) 398 | { 399 | return i; 400 | } 401 | } 402 | } 403 | 404 | void get_error_reponse(char *buffer, char *response) 405 | { 406 | if(is_number(buffer)) 407 | { 408 | sprintf(response, "Cannot find id = %s", buffer); 409 | } 410 | else 411 | { 412 | sprintf(response, "Cannot find an app named : %s", buffer); 413 | } 414 | } 415 | 416 | void server_start_process_and_get_response(char *buffer, char* response) 417 | { 418 | int i; 419 | int type; 420 | int pos; 421 | process_s process; 422 | 423 | type = parse_process_and_process_type(buffer, &process); 424 | 425 | switch(type) 426 | { 427 | case TYPE_CMD: 428 | /*check out duplication of app_name*/ 429 | for (i = 0; i < process_count; i++) 430 | { 431 | if (strcmp(process.app_name, process_list[i].app_name) == 0) 432 | { 433 | sprintf(response, "Duplicated name : %s. Please rename the program.", process.app_name); 434 | return; 435 | } 436 | } 437 | server_start_process_with_cmd(&process, response); 438 | break; 439 | case TYPE_ID: 440 | case TYPE_APP_NAME: 441 | if (!process.is_running) 442 | { 443 | pos = get_pos_in_process_list(&process); 444 | server_start_process(&process_list[pos], 1); 445 | sprintf(response, "Started %s with pid:%d.",process.app_name, process_list[pos].pid); 446 | } 447 | else 448 | { 449 | sprintf(response, "%s have already been running.", process.app_name); 450 | } 451 | 452 | break; 453 | default: 454 | get_error_reponse(buffer, response); 455 | break; 456 | } 457 | 458 | } 459 | 460 | void server_stop_process_and_get_response(char *buffer, char* response) 461 | { 462 | int type; 463 | int pos; 464 | process_s process; 465 | 466 | type = parse_process_and_process_type(buffer, &process); 467 | switch(type) 468 | { 469 | case TYPE_ID: 470 | case TYPE_APP_NAME: 471 | if (process.is_running) 472 | { 473 | pos = get_pos_in_process_list(&process); 474 | server_stop_process(&process_list[pos]); 475 | sprintf(response, "Stopped %s success.",process.app_name); 476 | } 477 | else 478 | { 479 | sprintf(response, "%s is not running.", process.app_name); 480 | } 481 | break; 482 | default: 483 | get_error_reponse(buffer, response); 484 | break; 485 | } 486 | } 487 | 488 | void server_restart_process_and_get_response(char *buffer, char* response) 489 | { 490 | int type; 491 | int pos; 492 | process_s process; 493 | 494 | type = parse_process_and_process_type(buffer, &process); 495 | switch(type) 496 | { 497 | case TYPE_ID: 498 | case TYPE_APP_NAME: 499 | if (process.is_running) 500 | { 501 | pos = get_pos_in_process_list(&process); 502 | server_restart_process(&process_list[pos]); 503 | sprintf(response, "Restarted %s with %d.",process.app_name, process_list[pos].pid); 504 | } 505 | else 506 | { 507 | sprintf(response, "%s is not running.", process.app_name); 508 | } 509 | break; 510 | default: 511 | get_error_reponse(buffer, response); 512 | break; 513 | } 514 | } 515 | 516 | void server_remove_process_and_get_response(char *buffer, char* response) 517 | { 518 | int type; 519 | int pos; 520 | process_s process; 521 | char app_name[STR_BUFFER_SIZE]; 522 | 523 | type = parse_process_and_process_type(buffer, &process); 524 | switch(type) 525 | { 526 | case TYPE_ID: 527 | case TYPE_APP_NAME: 528 | pos = get_pos_in_process_list(&process); 529 | server_remove_process(&process_list[pos], app_name); 530 | if (process.is_running) 531 | { 532 | sprintf(response, "Stopped And Removed %s.",app_name); 533 | } 534 | else 535 | { 536 | sprintf(response, "Removed %s.", app_name); 537 | } 538 | break; 539 | default: 540 | get_error_reponse(buffer, response); 541 | break; 542 | } 543 | } 544 | 545 | void server_start_process_with_cmd(process_s *process, char* response) 546 | { 547 | server_start_process(process, 1); 548 | /*add process at tail of process_list*/ 549 | process->id = process_count; 550 | process_list[process_count] = *process; 551 | process_count++; 552 | sprintf(response, "Starting %s with pid:%d.",process->app_name, process->pid); 553 | } 554 | 555 | void server_start_process(process_s *process, int with_log) 556 | { 557 | int res; 558 | pid_t pid; 559 | 560 | pid = fork(); 561 | if(pid < 0) 562 | { 563 | syslog(LOG_ERR, "cannot fork(), error: %s", strerror(errno)); 564 | syslog(LOG_ERR, "server unexpected exit."); 565 | unlink(LOCAL_SOCKET_FILE); 566 | exit(EXIT_FAILURE); 567 | } 568 | else if(pid == 0)/*the child process*/ 569 | { 570 | ignore_signals(); 571 | /*exec the child process.*/ 572 | exec_process(process, &res); 573 | syslog(LOG_ERR, "%s unexpected exit: %s.", process->app_name, strerror(errno)); 574 | exit(EXIT_FAILURE); 575 | } 576 | else/*the parent process*/ 577 | { 578 | process->pid = pid; 579 | process->is_running = 1; 580 | process->restart_times = 0; 581 | time(&process->start_time); 582 | if(with_log) 583 | { 584 | syslog(LOG_INFO, "Starting %s with pid:%d.",process->app_name, process->pid); 585 | } 586 | } 587 | } 588 | 589 | void server_stop_process(process_s *process) 590 | { 591 | int res; 592 | 593 | if(process->is_running) 594 | { 595 | process->is_running = 0; 596 | kill_process(process, &res); 597 | process->pid = 0; 598 | process->start_time = 0; 599 | process->restart_times = 0; 600 | syslog(LOG_INFO, "Stopping %s.",process->app_name); 601 | } 602 | } 603 | 604 | void server_restart_process(process_s *process) 605 | { 606 | server_stop_process(process); 607 | server_start_process(process, 1); 608 | process->restart_times++; 609 | } 610 | 611 | void server_remove_process(process_s *process, char* app_name) 612 | { 613 | server_stop_process(process); 614 | syslog(LOG_INFO, "Removing %s.",process->app_name); 615 | 616 | strcpy(app_name, process->app_name); 617 | /*delete the process form list.*/ 618 | del_process_by_app_name(process_list, process->app_name); 619 | process_count--; 620 | 621 | } 622 | 623 | void server_start_all_process(void) 624 | { 625 | int i; 626 | process_s *process; 627 | 628 | for(i = 0; i < process_count; i++) 629 | { 630 | process = &process_list[i]; 631 | if(process->is_running) 632 | { 633 | continue; 634 | } 635 | server_start_process(process, 1); 636 | } 637 | } 638 | 639 | void server_stop_all_process(void) 640 | { 641 | int i; 642 | process_s *process; 643 | 644 | for(i = 0; i < process_count; i++) 645 | { 646 | process = &process_list[i]; 647 | if(process->is_running) 648 | { 649 | server_stop_process(process); 650 | } 651 | } 652 | } 653 | 654 | void get_process_list_status(char *response) 655 | { 656 | create_process_list_json_str_with_status(process_list, process_count, response); 657 | } 658 | 659 | int get_running_process_count() 660 | { 661 | int i, running_prcess_count = 0; 662 | process_s *process; 663 | for(i = 0; i < process_count; i++) 664 | { 665 | process = &process_list[i]; 666 | if(process->is_running) 667 | { 668 | running_prcess_count++; 669 | } 670 | } 671 | return running_prcess_count; 672 | } 673 | 674 | 675 | void create_config_file(void) 676 | { 677 | int status; 678 | char *home_path; 679 | char config_path[STR_BUFFER_SIZE]; 680 | char config_dir[STR_BUFFER_SIZE]; 681 | 682 | /*check out $HOME in system env.*/ 683 | home_path = getenv("HOME"); 684 | if (!home_path) 685 | { 686 | fprintf(stderr, "You have to set the env:$HOME\n"); 687 | exit(EXIT_FAILURE); 688 | } 689 | 690 | get_config_path(config_path); 691 | get_config_dir(config_dir); 692 | 693 | /*check out if the config file exsit.*/ 694 | if(access(config_path, F_OK) == -1) 695 | { 696 | /*create dir.*/ 697 | status = mkdir(config_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 698 | /*create file.`*/ 699 | status = open(config_path, O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 700 | } 701 | } 702 | 703 | void ignore_signals(void) 704 | { 705 | signal(SIGTTOU, SIG_IGN); 706 | signal(SIGTTIN, SIG_IGN); 707 | signal(SIGTSTP, SIG_IGN); 708 | signal(SIGHUP, SIG_IGN); 709 | } 710 | 711 | void init_daemon(void) 712 | { 713 | pid_t pid; 714 | int i; 715 | 716 | /*ignore some signals*/ 717 | ignore_signals(); 718 | 719 | pid = fork(); 720 | if(pid > 0) 721 | { 722 | exit(EXIT_SUCCESS); 723 | } 724 | else if(pid < 0) 725 | { 726 | perror("can not fork(), error:"); 727 | exit(EXIT_FAILURE); 728 | } 729 | 730 | if(setsid() < 0) 731 | { 732 | perror("can not setsid(), error:"); 733 | exit(EXIT_FAILURE); 734 | } 735 | 736 | pid = fork(); 737 | if(pid > 0) 738 | { 739 | exit(EXIT_SUCCESS); 740 | } 741 | else if(pid < 0) 742 | { 743 | perror("can not fork(), error:"); 744 | exit(EXIT_FAILURE); 745 | } 746 | 747 | /*close stdout, stdin, stderr*/ 748 | for(i = 0; i < NOFILE; i++) 749 | { 750 | close(i); 751 | } 752 | 753 | chdir("/tmp"); 754 | umask(0); 755 | 756 | openlog("KB_PM", LOG_PID, LOG_DAEMON); 757 | } -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_SERVER_H 26 | #define KBPM_SERVER_H 27 | 28 | #define SERVER_ACCEPT_COUNT 5 29 | /*config_path would be ~/.kbpm/process_list.json*/ 30 | #define CONFIG_PATH "/.kbpm/process_list.json" 31 | /*config_dir would be ~/.kbpm */ 32 | #define CONFIG_DIR "/.kbpm" 33 | 34 | #ifndef LOCAL_SOCKET_FILE 35 | #define LOCAL_SOCKET_FILE "/tmp/kbpm.sock" 36 | #endif /*LOCAL_SOCKET_FILE*/ 37 | 38 | #ifndef STR_BUFFER_SIZE 39 | #define STR_BUFFER_SIZE 4 * 1024 40 | #endif/*STR_BUFFER_SIZE*/ 41 | 42 | #ifndef BUFFER_SIZE 43 | #define BUFFER_SIZE 40 * 1024 44 | #endif /*BUFFER_SIZE*/ 45 | 46 | #define TYPE_APP_NAME 0 47 | #define TYPE_ID 1 48 | #define TYPE_CMD 2 49 | #define TYPE_ERR 3 50 | 51 | 52 | /*start the KB_PM service, let it run in background.*/ 53 | extern void service_start(void); 54 | 55 | /*resolve the KB_PM service unexpectedly exit.*/ 56 | extern void service_clean(void); 57 | 58 | #endif /*KBPM_SERVER_H*/ 59 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include "utils.h" 27 | 28 | /*split the str by spl, save into dst, and return the dst size.*/ 29 | int str_split(char dst[][STR_BUFFER_SIZE], char* str, const char* spl) 30 | { 31 | int n = 0; 32 | char *result = NULL; 33 | result = strtok(str, spl); 34 | while(result != NULL) 35 | { 36 | strcpy(dst[n++], result); 37 | result = strtok(NULL, spl); 38 | } 39 | return n; 40 | } 41 | 42 | /*check out if the buffer is number.*/ 43 | int is_number(char *buffer) 44 | { 45 | int i; 46 | for (i = 0; i < strlen(buffer); i++) 47 | { 48 | if (!(buffer[i] <= '9' && buffer[i] >= '0')) 49 | { 50 | return 0; 51 | } 52 | } 53 | return 1; 54 | } -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Kalen Blue 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef KBPM_UTILS_H 26 | #define KBPM_UTILS_H 27 | 28 | #ifndef STR_BUFFER_SIZE 29 | #define STR_BUFFER_SIZE 512 30 | #endif /*STR_BUFFER_SIZE*/ 31 | 32 | /*split the str by spl, save into dst, and return the dst size.*/ 33 | extern int str_split(char dst[][STR_BUFFER_SIZE], char* str, const char* spl); 34 | 35 | /*check out if the buffer is number.*/ 36 | extern int is_number(char *buffer); 37 | 38 | #endif /*KBPM_UTILS_H*/ 39 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while true 4 | do 5 | echo "Hello World" >> test.log 6 | sleep 2s 7 | done 8 | --------------------------------------------------------------------------------