├── .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 | [](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 | 
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 |
--------------------------------------------------------------------------------