├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── ae.c ├── ae.h ├── ae_epoll.c ├── ae_kqueue.c ├── ae_select.c ├── already_running.c ├── already_running.h ├── anet.c ├── anet.h ├── config.h ├── config_ipcam_info.c ├── config_ipcam_info.h ├── debug_print.h ├── fmacros.h ├── get_mac.c ├── get_mac.h ├── ipcam_list.c ├── ipcam_list.h ├── ipcam_message.c ├── ipcam_message.h ├── ipcam_search_device.c ├── ipcam_search_devicev2.c ├── ipcam_search_pc.c ├── ipcam_search_pcv2.c ├── ipcam_search_protocol.txt ├── para_parse.c ├── para_parse.h ├── socket_wrap.c ├── socket_wrap.h └── zmalloc.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.log 3 | *.tmp 4 | *.pid 5 | ipcam_search_device 6 | ipcam_search_devicev2 7 | ipcam_search_pc 8 | ipcam_search_pcv2 9 | get_mac_test.c 10 | tags 11 | *.out 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The BSD License 2 | 3 | Copyright (c) 2013 mingang.he \ 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of the Redis, Ipcam_search_protocol nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | debug = 2 | PLATFORM = $(shell uname -s) 3 | EXESUF = 4 | CFLAGS = -Wall -fno-strict-aliasing 5 | LIBS = -lpthread 6 | 7 | ifeq ($(PLATFORM), MINGW32_NT-5.1) 8 | WINLIBS = -lws2_32 -liphlpapi 9 | EXESUF = .exe 10 | else ifeq ($(PLATFORM), Linux) 11 | WINLIBS = 12 | CFLAGS += -D_LINUX_=1 13 | else 14 | $(error only for Linux or mingw PLATFORM.) 15 | endif 16 | ifeq ($(debug), 1) 17 | CFLAGS += -Wextra -O0 -DDEBUG=1 -g 18 | else 19 | CFLAGS += -O2 20 | endif 21 | 22 | LIBS += $(WINLIBS) 23 | CROSS_COMPILE = 24 | CC = gcc 25 | CC := $(CROSS_COMPILE)$(CC) 26 | STRIP ?= strip 27 | STRIP := $(CROSS_COMPILE)$(STRIP) 28 | 29 | TARGET = ipcam_search_pc ipcam_search_device ipcam_search_pcv2 \ 30 | ipcam_search_devicev2 31 | 32 | .PHONY: all clean 33 | 34 | all: $(TARGET) 35 | 36 | strip: all 37 | $(STRIP) $(patsubst %,%$(EXESUF),$(TARGET)) 38 | 39 | ipcam_search_pc: ipcam_search_pc.o ipcam_list.o ipcam_message.o \ 40 | socket_wrap.o 41 | $(CC) -o $@ $^ $(LIBS) 42 | 43 | ipcam_search_pc.o: ipcam_search_pc.c ipcam_message.h debug_print.h \ 44 | para_parse.o 45 | $(CC) -c -o $@ $< $(CFLAGS) 46 | 47 | ipcam_search_pcv2: ipcam_search_pcv2.o ipcam_list.o ipcam_message.o \ 48 | socket_wrap.o para_parse.o already_running.o 49 | $(CC) -o $@ $^ 50 | 51 | ipcam_search_pcv2.o: ipcam_search_pcv2.c ipcam_message.h debug_print.h 52 | $(CC) -c -o $@ $< $(CFLAGS) 53 | 54 | ipcam_list.o: ipcam_list.c ipcam_list.h debug_print.h 55 | $(CC) -c -o $@ $< $(CFLAGS) 56 | 57 | ipcam_search_device: ipcam_search_device.o get_mac.o ipcam_message.o \ 58 | socket_wrap.o config_ipcam_info.o para_parse.o 59 | $(CC) -o $@ $^ $(LIBS) 60 | 61 | ipcam_search_devicev2: ipcam_search_devicev2.o get_mac.o ipcam_message.o \ 62 | socket_wrap.o config_ipcam_info.o para_parse.o 63 | $(CC) -o $@ $^ 64 | 65 | para_parse.o: para_parse.c para_parse.h 66 | $(CC) -c -o $@ $< $(CFLAGS) 67 | 68 | config_ipcam_info.o: config_ipcam_info.c config_ipcam_info.h 69 | $(CC) -c -o $@ $< $(CFLAGS) 70 | 71 | socket_wrap.o: socket_wrap.c socket_wrap.h 72 | $(CC) -c -o $@ $< $(CFLAGS) 73 | 74 | get_mac.o: get_mac.c get_mac.h 75 | $(CC) -c -o $@ $< $(CFLAGS) 76 | 77 | ipcam_message.o: ipcam_message.c ipcam_message.h 78 | $(CC) -c -o $@ $< $(CFLAGS) 79 | 80 | already_running.o: already_running.c already_running.h 81 | $(CC) -c -o $@ $< $(CFLAGS) 82 | 83 | get_mac.h: debug_print.h 84 | 85 | ipcam_search_device.o: ipcam_search_device.c ipcam_message.h \ 86 | debug_print.h get_mac.h 87 | $(CC) -c -o $@ $< $(CFLAGS) 88 | 89 | ipcam_search_devicev2.o: ipcam_search_devicev2.c ipcam_message.h \ 90 | debug_print.h get_mac.h 91 | $(CC) -c -o $@ $< $(CFLAGS) 92 | 93 | clean: 94 | -rm -f $(TARGET:%=%$(EXESUF)) *.o 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ipcam_search_protocol 2 | ===================== 3 | 4 | search ip cam on network. 5 | 6 | 通信协议栈说明: 见[ipcam_search_protocol.txt](ipcam_search_protocol.txt) 7 | 8 | ## License 9 | 10 | 在 BSD License 许可协议下发布, 参见 [LICENSE.md](LICENSE.md). 11 | 12 | ## 使用方法 13 | 14 | 编译生成目标文件: 15 | 16 | make 17 | 18 | 19 | 生成的文件有: ipcam_search_pc, ipcam_search_device 20 | -------------------------------------------------------------------------------- /ae.c: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "ae.h" 40 | #include "zmalloc.h" 41 | #include "config.h" 42 | 43 | /* Include the best multiplexing layer supported by this system. 44 | * The following should be ordered by performances, descending. */ 45 | #ifdef HAVE_EPOLL 46 | #include "ae_epoll.c" 47 | #else 48 | #ifdef HAVE_KQUEUE 49 | #include "ae_kqueue.c" 50 | #else 51 | #include "ae_select.c" 52 | #endif 53 | #endif 54 | 55 | aeEventLoop *aeCreateEventLoop(void) { 56 | aeEventLoop *eventLoop; 57 | int i; 58 | 59 | eventLoop = zmalloc(sizeof(*eventLoop)); 60 | if (!eventLoop) return NULL; 61 | eventLoop->timeEventHead = NULL; 62 | eventLoop->timeEventNextId = 0; 63 | eventLoop->stop = 0; 64 | eventLoop->maxfd = -1; 65 | eventLoop->beforesleep = NULL; 66 | if (aeApiCreate(eventLoop) == -1) { 67 | zfree(eventLoop); 68 | return NULL; 69 | } 70 | /* Events with mask == AE_NONE are not set. So let's initialize the 71 | * vector with it. */ 72 | for (i = 0; i < AE_SETSIZE; i++) 73 | eventLoop->events[i].mask = AE_NONE; 74 | return eventLoop; 75 | } 76 | 77 | void aeDeleteEventLoop(aeEventLoop *eventLoop) { 78 | aeApiFree(eventLoop); 79 | zfree(eventLoop); 80 | } 81 | 82 | void aeStop(aeEventLoop *eventLoop) { 83 | eventLoop->stop = 1; 84 | } 85 | 86 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 87 | aeFileProc *proc, void *clientData) 88 | { 89 | if (fd >= AE_SETSIZE) return AE_ERR; 90 | aeFileEvent *fe = &eventLoop->events[fd]; 91 | 92 | if (aeApiAddEvent(eventLoop, fd, mask) == -1) 93 | return AE_ERR; 94 | fe->mask |= mask; 95 | if (mask & AE_READABLE) fe->rfileProc = proc; 96 | if (mask & AE_WRITABLE) fe->wfileProc = proc; 97 | fe->clientData = clientData; 98 | if (fd > eventLoop->maxfd) 99 | eventLoop->maxfd = fd; 100 | return AE_OK; 101 | } 102 | 103 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) 104 | { 105 | if (fd >= AE_SETSIZE) return; 106 | aeFileEvent *fe = &eventLoop->events[fd]; 107 | 108 | if (fe->mask == AE_NONE) return; 109 | fe->mask = fe->mask & (~mask); 110 | if (fd == eventLoop->maxfd && fe->mask == AE_NONE) { 111 | /* Update the max fd */ 112 | int j; 113 | 114 | for (j = eventLoop->maxfd-1; j >= 0; j--) 115 | if (eventLoop->events[j].mask != AE_NONE) break; 116 | eventLoop->maxfd = j; 117 | } 118 | aeApiDelEvent(eventLoop, fd, mask); 119 | } 120 | 121 | static void aeGetTime(long *seconds, long *milliseconds) 122 | { 123 | struct timeval tv; 124 | 125 | gettimeofday(&tv, NULL); 126 | *seconds = tv.tv_sec; 127 | *milliseconds = tv.tv_usec/1000; 128 | } 129 | 130 | static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) { 131 | long cur_sec, cur_ms, when_sec, when_ms; 132 | 133 | aeGetTime(&cur_sec, &cur_ms); 134 | when_sec = cur_sec + milliseconds/1000; 135 | when_ms = cur_ms + milliseconds%1000; 136 | if (when_ms >= 1000) { 137 | when_sec ++; 138 | when_ms -= 1000; 139 | } 140 | *sec = when_sec; 141 | *ms = when_ms; 142 | } 143 | 144 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 145 | aeTimeProc *proc, void *clientData, 146 | aeEventFinalizerProc *finalizerProc) 147 | { 148 | long long id = eventLoop->timeEventNextId++; 149 | aeTimeEvent *te; 150 | 151 | te = zmalloc(sizeof(*te)); 152 | if (te == NULL) return AE_ERR; 153 | te->id = id; 154 | aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 155 | te->timeProc = proc; 156 | te->finalizerProc = finalizerProc; 157 | te->clientData = clientData; 158 | te->next = eventLoop->timeEventHead; 159 | eventLoop->timeEventHead = te; 160 | return id; 161 | } 162 | 163 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) 164 | { 165 | aeTimeEvent *te, *prev = NULL; 166 | 167 | te = eventLoop->timeEventHead; 168 | while(te) { 169 | if (te->id == id) { 170 | if (prev == NULL) 171 | eventLoop->timeEventHead = te->next; 172 | else 173 | prev->next = te->next; 174 | if (te->finalizerProc) 175 | te->finalizerProc(eventLoop, te->clientData); 176 | zfree(te); 177 | return AE_OK; 178 | } 179 | prev = te; 180 | te = te->next; 181 | } 182 | return AE_ERR; /* NO event with the specified ID found */ 183 | } 184 | 185 | /* Search the first timer to fire. 186 | * This operation is useful to know how many time the select can be 187 | * put in sleep without to delay any event. 188 | * If there are no timers NULL is returned. 189 | * 190 | * Note that's O(N) since time events are unsorted. 191 | * Possible optimizations (not needed by Redis so far, but...): 192 | * 1) Insert the event in order, so that the nearest is just the head. 193 | * Much better but still insertion or deletion of timers is O(N). 194 | * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). 195 | */ 196 | static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) 197 | { 198 | aeTimeEvent *te = eventLoop->timeEventHead; 199 | aeTimeEvent *nearest = NULL; 200 | 201 | while(te) { 202 | if (!nearest || te->when_sec < nearest->when_sec || 203 | (te->when_sec == nearest->when_sec && 204 | te->when_ms < nearest->when_ms)) 205 | nearest = te; 206 | te = te->next; 207 | } 208 | return nearest; 209 | } 210 | 211 | /* Process time events */ 212 | static int processTimeEvents(aeEventLoop *eventLoop) { 213 | int processed = 0; 214 | aeTimeEvent *te; 215 | long long maxId; 216 | 217 | te = eventLoop->timeEventHead; 218 | maxId = eventLoop->timeEventNextId-1; 219 | while(te) { 220 | long now_sec, now_ms; 221 | long long id; 222 | 223 | if (te->id > maxId) { 224 | te = te->next; 225 | continue; 226 | } 227 | aeGetTime(&now_sec, &now_ms); 228 | if (now_sec > te->when_sec || 229 | (now_sec == te->when_sec && now_ms >= te->when_ms)) 230 | { 231 | int retval; 232 | 233 | id = te->id; 234 | retval = te->timeProc(eventLoop, id, te->clientData); 235 | processed++; 236 | /* After an event is processed our time event list may 237 | * no longer be the same, so we restart from head. 238 | * Still we make sure to don't process events registered 239 | * by event handlers itself in order to don't loop forever. 240 | * To do so we saved the max ID we want to handle. 241 | * 242 | * FUTURE OPTIMIZATIONS: 243 | * Note that this is NOT great algorithmically. Redis uses 244 | * a single time event so it's not a problem but the right 245 | * way to do this is to add the new elements on head, and 246 | * to flag deleted elements in a special way for later 247 | * deletion (putting references to the nodes to delete into 248 | * another linked list). */ 249 | if (retval != AE_NOMORE) { 250 | aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); 251 | } else { 252 | aeDeleteTimeEvent(eventLoop, id); 253 | } 254 | te = eventLoop->timeEventHead; 255 | } else { 256 | te = te->next; 257 | } 258 | } 259 | return processed; 260 | } 261 | 262 | /* Process every pending time event, then every pending file event 263 | * (that may be registered by time event callbacks just processed). 264 | * Without special flags the function sleeps until some file event 265 | * fires, or when the next time event occurrs (if any). 266 | * 267 | * If flags is 0, the function does nothing and returns. 268 | * if flags has AE_ALL_EVENTS set, all the kind of events are processed. 269 | * if flags has AE_FILE_EVENTS set, file events are processed. 270 | * if flags has AE_TIME_EVENTS set, time events are processed. 271 | * if flags has AE_DONT_WAIT set the function returns ASAP until all 272 | * the events that's possible to process without to wait are processed. 273 | * 274 | * The function returns the number of events processed. */ 275 | int aeProcessEvents(aeEventLoop *eventLoop, int flags) 276 | { 277 | int processed = 0, numevents; 278 | 279 | /* Nothing to do? return ASAP */ 280 | if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; 281 | 282 | /* Note that we want call select() even if there are no 283 | * file events to process as long as we want to process time 284 | * events, in order to sleep until the next time event is ready 285 | * to fire. */ 286 | if (eventLoop->maxfd != -1 || 287 | ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { 288 | int j; 289 | aeTimeEvent *shortest = NULL; 290 | struct timeval tv, *tvp; 291 | 292 | if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) 293 | shortest = aeSearchNearestTimer(eventLoop); 294 | if (shortest) { 295 | long now_sec, now_ms; 296 | 297 | /* Calculate the time missing for the nearest 298 | * timer to fire. */ 299 | aeGetTime(&now_sec, &now_ms); 300 | tvp = &tv; 301 | tvp->tv_sec = shortest->when_sec - now_sec; 302 | if (shortest->when_ms < now_ms) { 303 | tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000; 304 | tvp->tv_sec --; 305 | } else { 306 | tvp->tv_usec = (shortest->when_ms - now_ms)*1000; 307 | } 308 | if (tvp->tv_sec < 0) tvp->tv_sec = 0; 309 | if (tvp->tv_usec < 0) tvp->tv_usec = 0; 310 | } else { 311 | /* If we have to check for events but need to return 312 | * ASAP because of AE_DONT_WAIT we need to se the timeout 313 | * to zero */ 314 | if (flags & AE_DONT_WAIT) { 315 | tv.tv_sec = tv.tv_usec = 0; 316 | tvp = &tv; 317 | } else { 318 | /* Otherwise we can block */ 319 | tvp = NULL; /* wait forever */ 320 | } 321 | } 322 | 323 | numevents = aeApiPoll(eventLoop, tvp); 324 | for (j = 0; j < numevents; j++) { 325 | aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; 326 | int mask = eventLoop->fired[j].mask; 327 | int fd = eventLoop->fired[j].fd; 328 | int rfired = 0; 329 | 330 | /* note the fe->mask & mask & ... code: maybe an already processed 331 | * event removed an element that fired and we still didn't 332 | * processed, so we check if the event is still valid. */ 333 | if (fe->mask & mask & AE_READABLE) { 334 | rfired = 1; 335 | fe->rfileProc(eventLoop,fd,fe->clientData,mask); 336 | } 337 | if (fe->mask & mask & AE_WRITABLE) { 338 | if (!rfired || fe->wfileProc != fe->rfileProc) 339 | fe->wfileProc(eventLoop,fd,fe->clientData,mask); 340 | } 341 | processed++; 342 | } 343 | } 344 | /* Check time events */ 345 | if (flags & AE_TIME_EVENTS) 346 | processed += processTimeEvents(eventLoop); 347 | 348 | return processed; /* return the number of processed file/time events */ 349 | } 350 | 351 | /* Wait for millseconds until the given file descriptor becomes 352 | * writable/readable/exception */ 353 | int aeWait(int fd, int mask, long long milliseconds) { 354 | struct timeval tv; 355 | fd_set rfds, wfds, efds; 356 | int retmask = 0, retval; 357 | 358 | tv.tv_sec = milliseconds/1000; 359 | tv.tv_usec = (milliseconds%1000)*1000; 360 | FD_ZERO(&rfds); 361 | FD_ZERO(&wfds); 362 | FD_ZERO(&efds); 363 | 364 | if (mask & AE_READABLE) FD_SET(fd,&rfds); 365 | if (mask & AE_WRITABLE) FD_SET(fd,&wfds); 366 | if ((retval = select(fd+1, &rfds, &wfds, &efds, &tv)) > 0) { 367 | if (FD_ISSET(fd,&rfds)) retmask |= AE_READABLE; 368 | if (FD_ISSET(fd,&wfds)) retmask |= AE_WRITABLE; 369 | return retmask; 370 | } else { 371 | return retval; 372 | } 373 | } 374 | 375 | void aeMain(aeEventLoop *eventLoop) { 376 | eventLoop->stop = 0; 377 | while (!eventLoop->stop) { 378 | if (eventLoop->beforesleep != NULL) 379 | eventLoop->beforesleep(eventLoop); 380 | aeProcessEvents(eventLoop, AE_ALL_EVENTS); 381 | } 382 | } 383 | 384 | char *aeGetApiName(void) { 385 | return aeApiName(); 386 | } 387 | 388 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) { 389 | eventLoop->beforesleep = beforesleep; 390 | } 391 | -------------------------------------------------------------------------------- /ae.h: -------------------------------------------------------------------------------- 1 | /* A simple event-driven programming library. Originally I wrote this code 2 | * for the Jim's event-loop (Jim is a Tcl interpreter) but later translated 3 | * it in form of a library for easy reuse. 4 | * 5 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __AE_H__ 34 | #define __AE_H__ 35 | 36 | #define AE_SETSIZE (1024*10) /* Max number of fd supported */ 37 | 38 | #define AE_OK 0 39 | #define AE_ERR -1 40 | 41 | #define AE_NONE 0 42 | #define AE_READABLE 1 43 | #define AE_WRITABLE 2 44 | 45 | #define AE_FILE_EVENTS 1 46 | #define AE_TIME_EVENTS 2 47 | #define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS) 48 | #define AE_DONT_WAIT 4 49 | 50 | #define AE_NOMORE -1 51 | 52 | /* Macros */ 53 | #define AE_NOTUSED(V) ((void) V) 54 | 55 | struct aeEventLoop; 56 | 57 | /* Types and data structures */ 58 | typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); 59 | typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData); 60 | typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData); 61 | typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop); 62 | 63 | /* File event structure */ 64 | typedef struct aeFileEvent { 65 | int mask; /* one of AE_(READABLE|WRITABLE) */ 66 | aeFileProc *rfileProc; 67 | aeFileProc *wfileProc; 68 | void *clientData; 69 | } aeFileEvent; 70 | 71 | /* Time event structure */ 72 | typedef struct aeTimeEvent { 73 | long long id; /* time event identifier. */ 74 | long when_sec; /* seconds */ 75 | long when_ms; /* milliseconds */ 76 | aeTimeProc *timeProc; 77 | aeEventFinalizerProc *finalizerProc; 78 | void *clientData; 79 | struct aeTimeEvent *next; 80 | } aeTimeEvent; 81 | 82 | /* A fired event */ 83 | typedef struct aeFiredEvent { 84 | int fd; 85 | int mask; 86 | } aeFiredEvent; 87 | 88 | /* State of an event based program */ 89 | typedef struct aeEventLoop { 90 | int maxfd; 91 | long long timeEventNextId; 92 | aeFileEvent events[AE_SETSIZE]; /* Registered events */ 93 | aeFiredEvent fired[AE_SETSIZE]; /* Fired events */ 94 | aeTimeEvent *timeEventHead; 95 | int stop; 96 | void *apidata; /* This is used for polling API specific data */ 97 | aeBeforeSleepProc *beforesleep; 98 | } aeEventLoop; 99 | 100 | /* Prototypes */ 101 | aeEventLoop *aeCreateEventLoop(void); 102 | void aeDeleteEventLoop(aeEventLoop *eventLoop); 103 | void aeStop(aeEventLoop *eventLoop); 104 | int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, 105 | aeFileProc *proc, void *clientData); 106 | void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask); 107 | long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 108 | aeTimeProc *proc, void *clientData, 109 | aeEventFinalizerProc *finalizerProc); 110 | int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id); 111 | int aeProcessEvents(aeEventLoop *eventLoop, int flags); 112 | int aeWait(int fd, int mask, long long milliseconds); 113 | void aeMain(aeEventLoop *eventLoop); 114 | char *aeGetApiName(void); 115 | void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep); 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /ae_epoll.c: -------------------------------------------------------------------------------- 1 | /* Linux epoll(2) based ae.c module 2 | * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | 7 | typedef struct aeApiState { 8 | int epfd; 9 | struct epoll_event events[AE_SETSIZE]; 10 | } aeApiState; 11 | 12 | static int aeApiCreate(aeEventLoop *eventLoop) { 13 | aeApiState *state = zmalloc(sizeof(aeApiState)); 14 | 15 | if (!state) return -1; 16 | state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ 17 | if (state->epfd == -1) return -1; 18 | eventLoop->apidata = state; 19 | return 0; 20 | } 21 | 22 | static void aeApiFree(aeEventLoop *eventLoop) { 23 | aeApiState *state = eventLoop->apidata; 24 | 25 | close(state->epfd); 26 | zfree(state); 27 | } 28 | 29 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 30 | aeApiState *state = eventLoop->apidata; 31 | struct epoll_event ee; 32 | /* If the fd was already monitored for some event, we need a MOD 33 | * operation. Otherwise we need an ADD operation. */ 34 | int op = eventLoop->events[fd].mask == AE_NONE ? 35 | EPOLL_CTL_ADD : EPOLL_CTL_MOD; 36 | 37 | ee.events = 0; 38 | mask |= eventLoop->events[fd].mask; /* Merge old events */ 39 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 40 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 41 | ee.data.u64 = 0; /* avoid valgrind warning */ 42 | ee.data.fd = fd; 43 | if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; 44 | return 0; 45 | } 46 | 47 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { 48 | aeApiState *state = eventLoop->apidata; 49 | struct epoll_event ee; 50 | int mask = eventLoop->events[fd].mask & (~delmask); 51 | 52 | ee.events = 0; 53 | if (mask & AE_READABLE) ee.events |= EPOLLIN; 54 | if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; 55 | ee.data.u64 = 0; /* avoid valgrind warning */ 56 | ee.data.fd = fd; 57 | if (mask != AE_NONE) { 58 | epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); 59 | } else { 60 | /* Note, Kernel < 2.6.9 requires a non null event pointer even for 61 | * EPOLL_CTL_DEL. */ 62 | epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee); 63 | } 64 | } 65 | 66 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 67 | aeApiState *state = eventLoop->apidata; 68 | int retval, numevents = 0; 69 | 70 | retval = epoll_wait(state->epfd,state->events,AE_SETSIZE, 71 | tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); 72 | if (retval > 0) { 73 | int j; 74 | 75 | numevents = retval; 76 | for (j = 0; j < numevents; j++) { 77 | int mask = 0; 78 | struct epoll_event *e = state->events+j; 79 | 80 | if (e->events & EPOLLIN) mask |= AE_READABLE; 81 | if (e->events & EPOLLOUT) mask |= AE_WRITABLE; 82 | eventLoop->fired[j].fd = e->data.fd; 83 | eventLoop->fired[j].mask = mask; 84 | } 85 | } 86 | return numevents; 87 | } 88 | 89 | static char *aeApiName(void) { 90 | return "epoll"; 91 | } 92 | -------------------------------------------------------------------------------- /ae_kqueue.c: -------------------------------------------------------------------------------- 1 | /* Kqueue(2)-based ae.c module 2 | * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct aeApiState { 10 | int kqfd; 11 | struct kevent events[AE_SETSIZE]; 12 | } aeApiState; 13 | 14 | static int aeApiCreate(aeEventLoop *eventLoop) { 15 | aeApiState *state = zmalloc(sizeof(aeApiState)); 16 | 17 | if (!state) return -1; 18 | state->kqfd = kqueue(); 19 | if (state->kqfd == -1) return -1; 20 | eventLoop->apidata = state; 21 | 22 | return 0; 23 | } 24 | 25 | static void aeApiFree(aeEventLoop *eventLoop) { 26 | aeApiState *state = eventLoop->apidata; 27 | 28 | close(state->kqfd); 29 | zfree(state); 30 | } 31 | 32 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 33 | aeApiState *state = eventLoop->apidata; 34 | struct kevent ke; 35 | 36 | if (mask & AE_READABLE) { 37 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 38 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 39 | } 40 | if (mask & AE_WRITABLE) { 41 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 42 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 43 | } 44 | return 0; 45 | } 46 | 47 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 48 | aeApiState *state = eventLoop->apidata; 49 | struct kevent ke; 50 | 51 | if (mask & AE_READABLE) { 52 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 53 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 54 | } 55 | if (mask & AE_WRITABLE) { 56 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 57 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 58 | } 59 | } 60 | 61 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 62 | aeApiState *state = eventLoop->apidata; 63 | int retval, numevents = 0; 64 | 65 | if (tvp != NULL) { 66 | struct timespec timeout; 67 | timeout.tv_sec = tvp->tv_sec; 68 | timeout.tv_nsec = tvp->tv_usec * 1000; 69 | retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, &timeout); 70 | } else { 71 | retval = kevent(state->kqfd, NULL, 0, state->events, AE_SETSIZE, NULL); 72 | } 73 | 74 | if (retval > 0) { 75 | int j; 76 | 77 | numevents = retval; 78 | for(j = 0; j < numevents; j++) { 79 | int mask = 0; 80 | struct kevent *e = state->events+j; 81 | 82 | if (e->filter == EVFILT_READ) mask |= AE_READABLE; 83 | if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE; 84 | eventLoop->fired[j].fd = e->ident; 85 | eventLoop->fired[j].mask = mask; 86 | } 87 | } 88 | return numevents; 89 | } 90 | 91 | static char *aeApiName(void) { 92 | return "kqueue"; 93 | } 94 | -------------------------------------------------------------------------------- /ae_select.c: -------------------------------------------------------------------------------- 1 | /* Select()-based ae.c module 2 | * Copyright (C) 2009-2010 Salvatore Sanfilippo - antirez@gmail.com 3 | * Released under the BSD license. See the COPYING file for more info. */ 4 | 5 | #include 6 | 7 | typedef struct aeApiState { 8 | fd_set rfds, wfds; 9 | /* We need to have a copy of the fd sets as it's not safe to reuse 10 | * FD sets after select(). */ 11 | fd_set _rfds, _wfds; 12 | } aeApiState; 13 | 14 | static int aeApiCreate(aeEventLoop *eventLoop) { 15 | aeApiState *state = zmalloc(sizeof(aeApiState)); 16 | 17 | if (!state) return -1; 18 | FD_ZERO(&state->rfds); 19 | FD_ZERO(&state->wfds); 20 | eventLoop->apidata = state; 21 | return 0; 22 | } 23 | 24 | static void aeApiFree(aeEventLoop *eventLoop) { 25 | zfree(eventLoop->apidata); 26 | } 27 | 28 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 29 | aeApiState *state = eventLoop->apidata; 30 | 31 | if (mask & AE_READABLE) FD_SET(fd,&state->rfds); 32 | if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds); 33 | return 0; 34 | } 35 | 36 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 37 | aeApiState *state = eventLoop->apidata; 38 | 39 | if (mask & AE_READABLE) FD_CLR(fd,&state->rfds); 40 | if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds); 41 | } 42 | 43 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 44 | aeApiState *state = eventLoop->apidata; 45 | int retval, j, numevents = 0; 46 | 47 | memcpy(&state->_rfds,&state->rfds,sizeof(fd_set)); 48 | memcpy(&state->_wfds,&state->wfds,sizeof(fd_set)); 49 | 50 | retval = select(eventLoop->maxfd+1, 51 | &state->_rfds,&state->_wfds,NULL,tvp); 52 | if (retval > 0) { 53 | for (j = 0; j <= eventLoop->maxfd; j++) { 54 | int mask = 0; 55 | aeFileEvent *fe = &eventLoop->events[j]; 56 | 57 | if (fe->mask == AE_NONE) continue; 58 | if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds)) 59 | mask |= AE_READABLE; 60 | if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds)) 61 | mask |= AE_WRITABLE; 62 | eventLoop->fired[numevents].fd = j; 63 | eventLoop->fired[numevents].mask = mask; 64 | numevents++; 65 | } 66 | } 67 | return numevents; 68 | } 69 | 70 | static char *aeApiName(void) { 71 | return "select"; 72 | } 73 | -------------------------------------------------------------------------------- /already_running.c: -------------------------------------------------------------------------------- 1 | #include "already_running.h" 2 | 3 | #ifndef PATH_MAX 4 | #define PATH_MAX 1024 5 | #endif 6 | 7 | #define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) 8 | 9 | static char *get_cur_proc_basename(char *cur_proc_basename, size_t bufsiz); 10 | static char *get_cur_proc_dir(char *cur_proc_dir, size_t bufsiz); 11 | static int lockfile(int fd); 12 | 13 | static char *get_cur_proc_basename(char *cur_proc_basename, size_t bufsiz) 14 | { 15 | int count; 16 | 17 | count = readlink("/proc/self/exe", cur_proc_basename, bufsiz); 18 | if (count < 0) { 19 | perror("readlink"); 20 | exit(errno); 21 | } 22 | cur_proc_basename[count] = '\0'; 23 | strcpy(cur_proc_basename, basename(cur_proc_basename)); 24 | 25 | return cur_proc_basename; 26 | } 27 | 28 | static char *get_cur_proc_dir(char *cur_proc_dir, size_t bufsiz) 29 | { 30 | int count; 31 | 32 | count = readlink("/proc/self/exe", cur_proc_dir, bufsiz); 33 | if (count < 0) { 34 | perror("readlink"); 35 | exit(errno); 36 | } 37 | cur_proc_dir[count] = '\0'; 38 | dirname(cur_proc_dir); 39 | strcat(cur_proc_dir, "/"); 40 | 41 | return cur_proc_dir; 42 | } 43 | 44 | static int lockfile(int fd) 45 | { 46 | struct flock fl; 47 | 48 | fl.l_type = F_WRLCK; 49 | fl.l_start = 0; 50 | fl.l_whence = SEEK_SET; 51 | fl.l_len = 0; 52 | return(fcntl(fd, F_SETLK, &fl)); 53 | } /* int lockfile(int fd) */ 54 | 55 | int already_running(void) 56 | { 57 | char cur_proc_dir[PATH_MAX]; 58 | char cur_proc_basename[PATH_MAX]; 59 | int fd; 60 | char buf[16]; 61 | int ret; 62 | 63 | get_cur_proc_basename(cur_proc_basename, PATH_MAX); 64 | get_cur_proc_dir(cur_proc_dir, PATH_MAX); 65 | strcat(cur_proc_dir, "._"); 66 | strcat(cur_proc_dir, cur_proc_basename); 67 | strcat(cur_proc_dir, "_locked_.pid"); 68 | 69 | fd = open(cur_proc_dir, O_RDWR|O_CREAT, LOCKMODE); 70 | if (fd < 0) { 71 | fprintf(stderr, "can't open %s: %s\n", 72 | cur_proc_dir, strerror(errno)); 73 | exit(1); 74 | } 75 | if (lockfile(fd) < 0) { 76 | if (errno == EACCES || errno == EAGAIN) { 77 | close(fd); 78 | return 1; 79 | } 80 | fprintf(stderr, "can't open %s: %s\n", 81 | cur_proc_dir, strerror(errno)); 82 | exit(1); 83 | } 84 | ret = ftruncate(fd, 0); 85 | if (ret == -1) 86 | perror("ftruncate"); 87 | 88 | sprintf(buf, "%ld", (long)getpid()); 89 | ret = write(fd, buf, strlen(buf)+1); 90 | if (ret == -1) 91 | perror("write"); 92 | return 0; 93 | } /* int already_running(void) */ 94 | -------------------------------------------------------------------------------- /already_running.h: -------------------------------------------------------------------------------- 1 | #ifndef _ALREADY_RUNNING_H 2 | #define _ALREADY_RUNNING_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int already_running(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /anet.c: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "fmacros.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "anet.h" 48 | 49 | static void anetSetError(char *err, const char *fmt, ...) 50 | { 51 | va_list ap; 52 | 53 | if (!err) return; 54 | va_start(ap, fmt); 55 | vsnprintf(err, ANET_ERR_LEN, fmt, ap); 56 | va_end(ap); 57 | } 58 | 59 | int anetNonBlock(char *err, int fd) 60 | { 61 | int flags; 62 | 63 | /* Set the socket nonblocking. 64 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be 65 | * interrupted by a signal. */ 66 | if ((flags = fcntl(fd, F_GETFL)) == -1) { 67 | anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 68 | return ANET_ERR; 69 | } 70 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 71 | anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); 72 | return ANET_ERR; 73 | } 74 | return ANET_OK; 75 | } 76 | 77 | int anetTcpNoDelay(char *err, int fd) 78 | { 79 | int yes = 1; 80 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) 81 | { 82 | anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); 83 | return ANET_ERR; 84 | } 85 | return ANET_OK; 86 | } 87 | 88 | int anetSetSendBuffer(char *err, int fd, int buffsize) 89 | { 90 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) 91 | { 92 | anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); 93 | return ANET_ERR; 94 | } 95 | return ANET_OK; 96 | } 97 | 98 | int anetTcpKeepAlive(char *err, int fd) 99 | { 100 | int yes = 1; 101 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { 102 | anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 103 | return ANET_ERR; 104 | } 105 | return ANET_OK; 106 | } 107 | 108 | int anetResolve(char *err, char *host, char *ipbuf) 109 | { 110 | struct sockaddr_in sa; 111 | 112 | sa.sin_family = AF_INET; 113 | if (inet_aton(host, &sa.sin_addr) == 0) { 114 | struct hostent *he; 115 | 116 | he = gethostbyname(host); 117 | if (he == NULL) { 118 | anetSetError(err, "can't resolve: %s", host); 119 | return ANET_ERR; 120 | } 121 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 122 | } 123 | strcpy(ipbuf,inet_ntoa(sa.sin_addr)); 124 | return ANET_OK; 125 | } 126 | 127 | static int anetCreateSocket(char *err, int domain) { 128 | int s, on = 1; 129 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { 130 | anetSetError(err, "creating socket: %s", strerror(errno)); 131 | return ANET_ERR; 132 | } 133 | 134 | /* Make sure connection-intensive things like the redis benckmark 135 | * will be able to close/open sockets a zillion of times */ 136 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { 137 | anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); 138 | return ANET_ERR; 139 | } 140 | return s; 141 | } 142 | 143 | #define ANET_CONNECT_NONE 0 144 | #define ANET_CONNECT_NONBLOCK 1 145 | static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) 146 | { 147 | int s; 148 | struct sockaddr_in sa; 149 | 150 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 151 | return ANET_ERR; 152 | 153 | sa.sin_family = AF_INET; 154 | sa.sin_port = htons(port); 155 | if (inet_aton(addr, &sa.sin_addr) == 0) { 156 | struct hostent *he; 157 | 158 | he = gethostbyname(addr); 159 | if (he == NULL) { 160 | anetSetError(err, "can't resolve: %s", addr); 161 | close(s); 162 | return ANET_ERR; 163 | } 164 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 165 | } 166 | if (flags & ANET_CONNECT_NONBLOCK) { 167 | if (anetNonBlock(err,s) != ANET_OK) 168 | return ANET_ERR; 169 | } 170 | if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { 171 | if (errno == EINPROGRESS && 172 | flags & ANET_CONNECT_NONBLOCK) 173 | return s; 174 | 175 | anetSetError(err, "connect: %s", strerror(errno)); 176 | close(s); 177 | return ANET_ERR; 178 | } 179 | return s; 180 | } 181 | 182 | int anetTcpConnect(char *err, char *addr, int port) 183 | { 184 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE); 185 | } 186 | 187 | int anetTcpNonBlockConnect(char *err, char *addr, int port) 188 | { 189 | return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK); 190 | } 191 | 192 | int anetUnixGenericConnect(char *err, char *path, int flags) 193 | { 194 | int s; 195 | struct sockaddr_un sa; 196 | 197 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 198 | return ANET_ERR; 199 | 200 | sa.sun_family = AF_LOCAL; 201 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 202 | if (flags & ANET_CONNECT_NONBLOCK) { 203 | if (anetNonBlock(err,s) != ANET_OK) 204 | return ANET_ERR; 205 | } 206 | if (connect(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { 207 | if (errno == EINPROGRESS && 208 | flags & ANET_CONNECT_NONBLOCK) 209 | return s; 210 | 211 | anetSetError(err, "connect: %s", strerror(errno)); 212 | close(s); 213 | return ANET_ERR; 214 | } 215 | return s; 216 | } 217 | 218 | int anetUnixConnect(char *err, char *path) 219 | { 220 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE); 221 | } 222 | 223 | int anetUnixNonBlockConnect(char *err, char *path) 224 | { 225 | return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK); 226 | } 227 | 228 | /* Like read(2) but make sure 'count' is read before to return 229 | * (unless error or EOF condition is encountered) */ 230 | int anetRead(int fd, char *buf, int count) 231 | { 232 | int nread, totlen = 0; 233 | while(totlen != count) { 234 | nread = read(fd,buf,count-totlen); 235 | if (nread == 0) return totlen; 236 | if (nread == -1) return -1; 237 | totlen += nread; 238 | buf += nread; 239 | } 240 | return totlen; 241 | } 242 | 243 | /* Like write(2) but make sure 'count' is read before to return 244 | * (unless error is encountered) */ 245 | int anetWrite(int fd, char *buf, int count) 246 | { 247 | int nwritten, totlen = 0; 248 | while(totlen != count) { 249 | nwritten = write(fd,buf,count-totlen); 250 | if (nwritten == 0) return totlen; 251 | if (nwritten == -1) return -1; 252 | totlen += nwritten; 253 | buf += nwritten; 254 | } 255 | return totlen; 256 | } 257 | 258 | static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { 259 | if (bind(s,sa,len) == -1) { 260 | anetSetError(err, "bind: %s", strerror(errno)); 261 | close(s); 262 | return ANET_ERR; 263 | } 264 | if (listen(s, 511) == -1) { /* the magic 511 constant is from nginx */ 265 | anetSetError(err, "listen: %s", strerror(errno)); 266 | close(s); 267 | return ANET_ERR; 268 | } 269 | return ANET_OK; 270 | } 271 | 272 | int anetTcpServer(char *err, int port, char *bindaddr) 273 | { 274 | int s; 275 | struct sockaddr_in sa; 276 | 277 | if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) 278 | return ANET_ERR; 279 | 280 | memset(&sa,0,sizeof(sa)); 281 | sa.sin_family = AF_INET; 282 | sa.sin_port = htons(port); 283 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 284 | if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) { 285 | anetSetError(err, "invalid bind address"); 286 | close(s); 287 | return ANET_ERR; 288 | } 289 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 290 | return ANET_ERR; 291 | return s; 292 | } 293 | 294 | int anetUnixServer(char *err, char *path) 295 | { 296 | int s; 297 | struct sockaddr_un sa; 298 | 299 | if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR) 300 | return ANET_ERR; 301 | 302 | memset(&sa,0,sizeof(sa)); 303 | sa.sun_family = AF_LOCAL; 304 | strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); 305 | if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) 306 | return ANET_ERR; 307 | return s; 308 | } 309 | 310 | static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { 311 | int fd; 312 | while(1) { 313 | fd = accept(s,sa,len); 314 | if (fd == -1) { 315 | if (errno == EINTR) 316 | continue; 317 | else { 318 | anetSetError(err, "accept: %s", strerror(errno)); 319 | return ANET_ERR; 320 | } 321 | } 322 | break; 323 | } 324 | return fd; 325 | } 326 | 327 | int anetTcpAccept(char *err, int s, char *ip, int *port) { 328 | int fd; 329 | struct sockaddr_in sa; 330 | socklen_t salen = sizeof(sa); 331 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 332 | return ANET_ERR; 333 | 334 | if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); 335 | if (port) *port = ntohs(sa.sin_port); 336 | return fd; 337 | } 338 | 339 | int anetUnixAccept(char *err, int s) { 340 | int fd; 341 | struct sockaddr_un sa; 342 | socklen_t salen = sizeof(sa); 343 | if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) 344 | return ANET_ERR; 345 | 346 | return fd; 347 | } 348 | -------------------------------------------------------------------------------- /anet.h: -------------------------------------------------------------------------------- 1 | /* anet.c -- Basic TCP socket stuff made a bit less boring 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef ANET_H 32 | #define ANET_H 33 | 34 | #define ANET_OK 0 35 | #define ANET_ERR -1 36 | #define ANET_ERR_LEN 256 37 | 38 | #if defined(__sun) 39 | #define AF_LOCAL AF_UNIX 40 | #endif 41 | 42 | int anetTcpConnect(char *err, char *addr, int port); 43 | int anetTcpNonBlockConnect(char *err, char *addr, int port); 44 | int anetUnixConnect(char *err, char *path); 45 | int anetUnixNonBlockConnect(char *err, char *path); 46 | int anetRead(int fd, char *buf, int count); 47 | int anetResolve(char *err, char *host, char *ipbuf); 48 | int anetTcpServer(char *err, int port, char *bindaddr); 49 | int anetUnixServer(char *err, char *path); 50 | int anetTcpAccept(char *err, int serversock, char *ip, int *port); 51 | int anetUnixAccept(char *err, int serversock); 52 | int anetWrite(int fd, char *buf, int count); 53 | int anetNonBlock(char *err, int fd); 54 | int anetTcpNoDelay(char *err, int fd); 55 | int anetTcpKeepAlive(char *err, int fd); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H 2 | #define __CONFIG_H 3 | 4 | 5 | /* test for polling API */ 6 | #ifdef __linux__ 7 | #define HAVE_EPOLL 1 8 | #endif 9 | 10 | #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__) 11 | #define HAVE_KQUEUE 1 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /config_ipcam_info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "config_ipcam_info.h" 3 | 4 | char *get_ipcam_name(char *ipcam_name) 5 | { 6 | /* 7 | * not finished 8 | */ 9 | strncpy(ipcam_name, "ipcam_name_test", 32); 10 | 11 | return ipcam_name; 12 | } 13 | 14 | int set_ipcam_name(const char *ipcam_name) 15 | { 16 | /* 17 | * not finished 18 | */ 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /config_ipcam_info.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG_IPCAM_INFO_H 2 | #define _CONFIG_IPCAM_INFO_H 3 | 4 | char *get_ipcam_name(char *ipcam_name); 5 | int set_ipcam_name(const char *ipcam_name); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /debug_print.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBUG_PRINT_H 2 | #define _DEBUG_PRINT_H 3 | 4 | #include 5 | #include 6 | 7 | #if DEBUG 8 | #define DEBUG_PRINT 1 9 | #else 10 | #define DEBUG_PRINT 0 11 | #endif 12 | 13 | #define debug_print(fmt, ...) \ 14 | do { \ 15 | if (DEBUG_PRINT) \ 16 | fprintf(stderr, "debug_print: %s: %d: %s():" \ 17 | fmt "\n", __FILE__, __LINE__, __func__, \ 18 | ##__VA_ARGS__); \ 19 | } while (0) 20 | 21 | #define DEBUG_LOG 1 22 | #if DEBUG_LOG 23 | FILE *DEBUG_LOG_FILE; 24 | char DEBUG_STRING[1024]; 25 | #else 26 | FILE *DEBUG_LOG_FILE; 27 | char *DEBUG_STRING; 28 | #endif 29 | 30 | #define open_debug_log(filename) do { \ 31 | if (DEBUG_LOG) { \ 32 | DEBUG_LOG_FILE = fopen(filename, "w"); \ 33 | } \ 34 | } while (0) 35 | 36 | #define debug_log(fmt, ...) do { \ 37 | if (DEBUG_LOG) { \ 38 | snprintf(DEBUG_STRING, 1023, "%s: %s(): %d: " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__); \ 39 | fwrite(DEBUG_STRING, 1, strlen(DEBUG_STRING), DEBUG_LOG_FILE); \ 40 | fflush(DEBUG_LOG_FILE); \ 41 | } \ 42 | } while (0) 43 | 44 | #define close_debug_log() do { \ 45 | if (DEBUG_LOG) { \ 46 | fclose(DEBUG_LOG_FILE); \ 47 | } \ 48 | } while (0) 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /fmacros.h: -------------------------------------------------------------------------------- 1 | #ifndef _REDIS_FMACRO_H 2 | #define _REDIS_FMACRO_H 3 | 4 | #ifndef _BSD_SOURCE 5 | #define _BSD_SOURCE 6 | #endif 7 | 8 | #if defined(__linux__) || defined(__GLIBC__) 9 | #define _XOPEN_SOURCE 700 10 | #else 11 | #define _XOPEN_SOURCE 12 | #endif 13 | 14 | #define _LARGEFILE_SOURCE 15 | #define _FILE_OFFSET_BITS 64 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /get_mac.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "get_mac.h" 4 | #include "debug_print.h" 5 | 6 | uint8_t *get_mac(uint8_t *str_mac) 7 | { 8 | #if _LINUX_ 9 | int sock; 10 | struct ifreq ifr; 11 | struct ifconf ifc; 12 | uint8_t buf[1024]; 13 | int success = 0; 14 | 15 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 16 | if (sock == -1) { 17 | debug_print("socket fail"); 18 | exit(errno); 19 | } 20 | memset(&ifr, 0, sizeof(ifr)); 21 | ifc.ifc_len = sizeof(buf); 22 | ifc.ifc_buf = (char *)buf; 23 | if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ 24 | debug_print("ioctl fail"); 25 | exit(errno); 26 | } 27 | 28 | struct ifreq *it = ifc.ifc_req; 29 | const struct ifreq * const end = it 30 | + (ifc.ifc_len / sizeof(struct ifreq)); 31 | 32 | for (; it != end; ++it) { 33 | strcpy(ifr.ifr_name, it->ifr_name); 34 | if (!ioctl(sock, SIOCGIFFLAGS, &ifr)) { 35 | if (!(ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback 36 | if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) { 37 | success = 1; 38 | break; 39 | } 40 | } 41 | } else { /* handle error */ 42 | debug_print("ioctl fail"); 43 | exit(errno); 44 | } 45 | } 46 | 47 | if (success) { 48 | memcpy(str_mac, ifr.ifr_hwaddr.sa_data, 6); 49 | return str_mac; 50 | } else 51 | return NULL; 52 | #else 53 | IP_ADAPTER_INFO AdapterInfo[16]; 54 | DWORD dwBufLen = sizeof(AdapterInfo); 55 | 56 | DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen); 57 | assert(dwStatus == ERROR_SUCCESS); 58 | 59 | PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; 60 | memcpy(str_mac, pAdapterInfo->Address, 6); 61 | 62 | return str_mac; 63 | #endif 64 | } 65 | 66 | -------------------------------------------------------------------------------- /get_mac.h: -------------------------------------------------------------------------------- 1 | #ifndef _GET_MAC_H 2 | #define _GET_MAC_H 3 | 4 | #include 5 | #include 6 | #if _LINUX_ 7 | #include 8 | #include 9 | #include 10 | #else 11 | #include 12 | #include 13 | #include 14 | #define WIN32_LEAN_AND_MEAN 15 | #endif 16 | #include 17 | #include 18 | 19 | uint8_t *get_mac(uint8_t *str_mac); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /ipcam_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ipcam_list.h" 5 | #include "debug_print.h" 6 | 7 | ipcam_link create_empty_ipcam_link(void) 8 | { 9 | ipcam_link link0 = malloc(sizeof(struct ipcam_node)); 10 | if (link0) 11 | link0->next = NULL; 12 | else { 13 | debug_print("out of space!"); 14 | exit(errno); 15 | } 16 | return link0; 17 | } 18 | 19 | ipcam_link insert_ipcam_node(ipcam_link link, const pipcam_node insert_node) 20 | { 21 | pipcam_node q = malloc(sizeof(struct ipcam_node)); 22 | memcpy(q, insert_node, sizeof(struct ipcam_node)); 23 | q->next = link->next; 24 | link->next = q; 25 | return link; 26 | } 27 | 28 | int delete_ipcam_all_node(ipcam_link link) 29 | { 30 | int ret = 0; 31 | pipcam_node *curr = &link; 32 | pipcam_node entry; 33 | 34 | while ((entry = (*curr)->next) != NULL) { 35 | (*curr)->next = entry->next; 36 | free(entry); 37 | ret++; 38 | } 39 | return ret; 40 | } 41 | 42 | int delete_ipcam_node_by_mac(ipcam_link link, const char *mac) 43 | { 44 | int ret = 0; 45 | pipcam_node *curr; 46 | pipcam_node nextnode; 47 | 48 | for (curr = &link; *curr; ) { 49 | nextnode = (*curr)->next; 50 | if (nextnode == NULL) 51 | break; 52 | if (!strncmp((const char *)nextnode->node_info.mac, mac, 53 | sizeof(nextnode->node_info.mac))) { 54 | (*curr)->next = nextnode->next; 55 | free(nextnode); 56 | ret++; 57 | } else 58 | curr = &nextnode; 59 | } 60 | return ret; 61 | } 62 | 63 | ipcam_link delete_this_ipcam_node(ipcam_link link, const pipcam_node this_node) 64 | { 65 | pipcam_node *curr; 66 | pipcam_node entry; 67 | 68 | for (curr = &link; (*curr)->next; ) { 69 | entry = (*curr)->next; 70 | if (entry == this_node) { 71 | *curr = entry->next; 72 | free(entry); 73 | } else 74 | curr = &entry; 75 | } 76 | return link; 77 | } 78 | 79 | int strvalncmp(const uint8_t *s1, const uint8_t *s2, size_t n) 80 | { 81 | size_t i; 82 | int ret = 0; 83 | 84 | for (i = 0; i < n; i++) { 85 | ret = s1[i] - s2[i]; 86 | if (ret) 87 | return ret; 88 | } 89 | return 0; 90 | } 91 | 92 | pipcam_node search_ipcam_node_by_mac(ipcam_link link, const uint8_t *mac) 93 | { 94 | pipcam_node q = link->next; 95 | 96 | while (q) { 97 | if (!strvalncmp(q->node_info.mac, mac, 98 | sizeof(q->node_info.mac))) 99 | return q; 100 | q = q->next; 101 | } 102 | return NULL; 103 | } 104 | 105 | int num_ipcam_node(const ipcam_link link) 106 | { 107 | int n = 0; 108 | ipcam_link *curr; 109 | 110 | for (curr = (ipcam_link *)&link; (*curr)->next; curr = &((*curr)->next)) 111 | n++; 112 | return n; 113 | } 114 | 115 | /* 116 | * return 0: fail 117 | * return 1: succeed 118 | */ 119 | int insert_nodulp_ipcam_node(ipcam_link link, const pipcam_node insert_node) 120 | { 121 | int ret = 0; 122 | if (!search_ipcam_node_by_mac(link, 123 | insert_node->node_info.mac)) { 124 | insert_ipcam_node(link, insert_node); 125 | ret = 1; 126 | } 127 | return ret; 128 | } 129 | 130 | void free_ipcam_link(ipcam_link link) 131 | { 132 | pipcam_node q = link->next; 133 | 134 | if (!link->next) 135 | return; 136 | else 137 | q = link->next->next; 138 | 139 | do { 140 | free(link->next); 141 | if (q) { 142 | link->next = q; 143 | q = q->next; 144 | } else 145 | break; 146 | } while (1); 147 | return; 148 | } 149 | -------------------------------------------------------------------------------- /ipcam_list.h: -------------------------------------------------------------------------------- 1 | #ifndef _IPCAM_LIST_H 2 | #define _IPCAM_LIST_H 3 | 4 | #include 5 | #if _LINUX_ 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | typedef struct ipcam_info { 12 | struct in_addr ipaddr; 13 | uint8_t mac[6]; 14 | uint8_t ipcam_name[64]; 15 | uint32_t startup_time; 16 | } ipcam_info_t; 17 | 18 | struct ipcam_node { 19 | ipcam_info_t node_info; 20 | int alive_flag; /* 0: not online, 1: online */ 21 | struct ipcam_node *next; 22 | }; 23 | 24 | typedef struct ipcam_node *pipcam_node; 25 | typedef struct ipcam_node *ipcam_link; 26 | 27 | int strvalncmp(const uint8_t *s1, const uint8_t *s2, size_t n); 28 | ipcam_link create_empty_ipcam_link(void); 29 | ipcam_link insert_ipcam_node(ipcam_link link, const pipcam_node insert_node); 30 | int delete_ipcam_all_node(ipcam_link link); 31 | int delete_ipcam_node_by_mac(ipcam_link link, const char *mac); 32 | ipcam_link delete_this_ipcam_node(ipcam_link link, const pipcam_node this_node); 33 | pipcam_node search_ipcam_node_by_mac(ipcam_link link, const uint8_t *mac); 34 | int num_ipcam_node(const ipcam_link link); 35 | int insert_nodulp_ipcam_node(ipcam_link link, const pipcam_node insert_node); 36 | void free_ipcam_link(ipcam_link link); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /ipcam_message.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ipcam_message.h" 3 | 4 | void parse_msg(const char *msg, int size, struct ipcam_search_msg *save_msg) 5 | { 6 | int exten_len; 7 | 8 | exten_len = ((struct ipcam_search_msg *)msg)->exten_len; 9 | save_msg->type = ((struct ipcam_search_msg *)msg)->type; 10 | save_msg->deal_id = ((struct ipcam_search_msg *)msg)->deal_id; 11 | save_msg->flag = ((struct ipcam_search_msg *)msg)->flag; 12 | save_msg->exten_len = exten_len; 13 | save_msg->ssrc = ((struct ipcam_search_msg *)msg)->ssrc; 14 | save_msg->timestamp = ((struct ipcam_search_msg *)msg)->timestamp; 15 | save_msg->heartbeat_num = ((struct ipcam_search_msg *)msg)->heartbeat_num; 16 | memcpy(save_msg->ipcam_name, 17 | ((struct ipcam_search_msg *)msg)->ipcam_name, 18 | sizeof(save_msg->ipcam_name)); 19 | if (exten_len) { 20 | memcpy(save_msg->exten_msg, 21 | ((struct ipcam_search_msg *)msg)->exten_msg, 22 | exten_len); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ipcam_message.h: -------------------------------------------------------------------------------- 1 | #ifndef _IPCAM_MESSAGE_H 2 | #define _IPCAM_MESSAGE_H 3 | 4 | #include 5 | 6 | struct ipcam_search_msg { 7 | uint8_t type; 8 | uint8_t deal_id; 9 | uint8_t flag; 10 | uint8_t exten_len; 11 | uint32_t ssrc; 12 | uint32_t timestamp; 13 | uint32_t heartbeat_num; 14 | char ipcam_name[64]; 15 | char exten_msg[0]; 16 | } __attribute ((packed)); 17 | 18 | enum { 19 | IPCAMMSG_LOGIN = 0x1, 20 | IPCAMMSG_LOGOUT, 21 | IPCAMMSG_HEARTBEAT, 22 | IPCAMMSG_QUERY_ALIVE, 23 | IPCAMMSG_ACK_ALIVE, 24 | IPCAMMSG_DHCP, 25 | IPCAMMSG_ACK_DHCP, 26 | IPCAMMSG_SET_IP, 27 | IPCAMMSG_ACK_IP, 28 | } ipcam_search_msg_type; 29 | 30 | void parse_msg(const char *msg, int size, struct ipcam_search_msg *save_msg); 31 | #endif 32 | -------------------------------------------------------------------------------- /ipcam_search_device.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #if _LINUX_ 10 | #include 11 | #include 12 | #include 13 | #else 14 | #include 15 | #define sleep(n) Sleep(1000 * (n)) 16 | #endif 17 | #include 18 | #include 19 | #include "ipcam_message.h" 20 | #include "socket_wrap.h" 21 | #include "get_mac.h" 22 | #include "config_ipcam_info.h" 23 | #include "para_parse.h" 24 | #include "debug_print.h" 25 | 26 | #define IPCAM_SERVER_PORT 6755 27 | #define PC_SERVER_PORT 6756 28 | #define MAX_MSG_LEN 512 29 | #define HEARTBEAT_CYCLE 10 /* 10s */ 30 | 31 | static int IPCAM_SERVER_FD = -1; 32 | static int IPCAM_CLIENT_FD = -1; 33 | static time_t STARTUP_TIME; 34 | static uint32_t SSRC; 35 | static uint8_t IPCAM_MAC[6]; 36 | 37 | int initsocket(void); 38 | static void replay_alive_msg(const struct ipcam_search_msg *recv_msg, const struct sockaddr_in *from); 39 | static void ipcam_deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from); 40 | static void *recv_msg_from_pc(void *p); 41 | void broadcast_login_msg(void); 42 | static void send_heartbeat_msg(void); 43 | static void send_logout_msg(void); 44 | static void release_exit(int signo); 45 | 46 | int initsocket(void) 47 | { 48 | int ret; 49 | struct sockaddr_in ipcam_server; 50 | 51 | #if !_LINUX_ 52 | WSADATA wsadata; 53 | if (WSAStartup(MAKEWORD(1, 1), &wsadata) == SOCKET_ERROR) { 54 | debug_print("WSAStartup() fail\n"); 55 | exit(errno); 56 | } 57 | #endif 58 | 59 | IPCAM_CLIENT_FD = socket(AF_INET, SOCK_DGRAM, 0); 60 | if (IPCAM_CLIENT_FD == -1) { 61 | debug_log("socket fail"); 62 | exit(errno); 63 | } 64 | 65 | IPCAM_SERVER_FD = socket(AF_INET, SOCK_DGRAM, 0); 66 | if (IPCAM_SERVER_FD == -1) { 67 | debug_log("socket fail"); 68 | exit(errno); 69 | } 70 | 71 | ipcam_server.sin_family = AF_INET; 72 | ipcam_server.sin_port = htons(IPCAM_SERVER_PORT); 73 | ipcam_server.sin_addr.s_addr = htonl(INADDR_ANY); 74 | 75 | ret = bind(IPCAM_SERVER_FD, (struct sockaddr*)&ipcam_server, 76 | sizeof(ipcam_server)); 77 | if (ret == -1) { 78 | debug_log("bind fail"); 79 | exit(errno); 80 | } 81 | 82 | return 0; 83 | } /* int initsocket(void) */ 84 | 85 | static void replay_alive_msg(const struct ipcam_search_msg *recv_msg, const struct sockaddr_in *from) 86 | { 87 | struct sockaddr_in remote_sockaddr; 88 | struct ipcam_search_msg send_msg = {0}; 89 | uint8_t buf[MAX_MSG_LEN]; 90 | 91 | send_msg.type = IPCAMMSG_ACK_ALIVE; 92 | send_msg.deal_id = recv_msg->deal_id; 93 | send_msg.ssrc = SSRC; 94 | send_msg.timestamp = time(NULL); 95 | get_ipcam_name(send_msg.ipcam_name); 96 | memcpy(buf, &send_msg, sizeof(send_msg)); 97 | memcpy(buf + sizeof(send_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 98 | memcpy(buf + sizeof(send_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 99 | sizeof(STARTUP_TIME)); 100 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 101 | + sizeof(STARTUP_TIME); 102 | 103 | memcpy(&remote_sockaddr, from, sizeof(remote_sockaddr)); 104 | remote_sockaddr.sin_port = htons(PC_SERVER_PORT); 105 | send_msg_by_sockaddr(buf, sizeof(send_msg) + 106 | ((struct ipcam_search_msg *)buf)->exten_len, &remote_sockaddr); 107 | 108 | return; 109 | } 110 | 111 | static void ipcam_deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from) 112 | { 113 | switch (msg->type) { 114 | case IPCAMMSG_QUERY_ALIVE: 115 | replay_alive_msg(msg, from); 116 | break; 117 | default: 118 | break; 119 | } 120 | 121 | return; 122 | } 123 | 124 | static void *recv_msg_from_pc(void *p) 125 | { 126 | int ret; 127 | struct sockaddr_in peer; 128 | uint8_t buf[MAX_MSG_LEN]; 129 | struct ipcam_search_msg *msg_buf; 130 | socklen_t len; 131 | 132 | while (1) { 133 | len = sizeof(struct sockaddr_in); 134 | ret = recvfrom(IPCAM_SERVER_FD, buf, sizeof(buf), 0, 135 | (struct sockaddr *)&peer, 136 | (socklen_t *)&len); 137 | if (ret < 0) { 138 | debug_log("recvfrom fail"); 139 | continue; 140 | } 141 | msg_buf = malloc(sizeof(struct ipcam_search_msg) 142 | + ((struct ipcam_search_msg *)buf)->exten_len); 143 | parse_msg((const char *)buf, ret, msg_buf); 144 | ipcam_deal_msg_func(msg_buf, &peer); 145 | 146 | if (msg_buf) { 147 | free(msg_buf); 148 | msg_buf = NULL; 149 | } 150 | } 151 | return NULL; 152 | } 153 | 154 | void broadcast_login_msg(void) 155 | { 156 | int ret; 157 | struct ipcam_search_msg login_msg; 158 | uint8_t buf[MAX_MSG_LEN]; 159 | 160 | debug_log("mac is %02x:%02x:%02x:%02x:%02x:%02x", 161 | IPCAM_MAC[0], 162 | IPCAM_MAC[1], 163 | IPCAM_MAC[2], 164 | IPCAM_MAC[3], 165 | IPCAM_MAC[4], 166 | IPCAM_MAC[5]); 167 | 168 | memset(&login_msg, 0, sizeof(login_msg)); 169 | login_msg.type = IPCAMMSG_LOGIN; 170 | login_msg.ssrc = SSRC; 171 | login_msg.timestamp = time(NULL); 172 | get_ipcam_name(login_msg.ipcam_name); 173 | 174 | memcpy(buf, &login_msg, sizeof(login_msg)); 175 | memcpy(buf + sizeof(login_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 176 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC); 177 | ret = broadcast_msg(PC_SERVER_PORT, buf, sizeof(login_msg) + sizeof(IPCAM_MAC)); 178 | debug_log("sendto return %d", ret); 179 | 180 | return; 181 | } 182 | 183 | static void send_logout_msg(void) 184 | { 185 | int ret; 186 | struct ipcam_search_msg logout_msg; 187 | uint8_t buf[MAX_MSG_LEN]; 188 | 189 | memset(&logout_msg, 0, sizeof(logout_msg)); 190 | 191 | logout_msg.type = IPCAMMSG_LOGOUT; 192 | logout_msg.ssrc = SSRC; 193 | logout_msg.timestamp = time(NULL); 194 | get_ipcam_name(logout_msg.ipcam_name); 195 | 196 | memcpy(buf, &logout_msg, sizeof(logout_msg)); 197 | memcpy(buf + sizeof(logout_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 198 | 199 | memcpy(buf + sizeof(logout_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 200 | sizeof(STARTUP_TIME)); 201 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 202 | + sizeof(STARTUP_TIME); 203 | 204 | ret = broadcast_msg(PC_SERVER_PORT, buf, 205 | sizeof(logout_msg) + ((struct ipcam_search_msg *)buf)->exten_len); 206 | if (ret < 0) 207 | debug_log("broadcast_msg fail"); 208 | } 209 | 210 | static void send_heartbeat_msg(void) 211 | { 212 | int ret; 213 | struct ipcam_search_msg heartbeat_msg; 214 | uint8_t buf[MAX_MSG_LEN]; 215 | 216 | memset(&heartbeat_msg, 0, sizeof(heartbeat_msg)); 217 | 218 | heartbeat_msg.type = IPCAMMSG_HEARTBEAT; 219 | heartbeat_msg.ssrc = SSRC; 220 | heartbeat_msg.timestamp = time(NULL); 221 | heartbeat_msg.heartbeat_num = 0; 222 | get_ipcam_name(heartbeat_msg.ipcam_name); 223 | 224 | memcpy(buf, &heartbeat_msg, sizeof(heartbeat_msg)); 225 | memcpy(buf + sizeof(heartbeat_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 226 | memcpy(buf + sizeof(heartbeat_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 227 | sizeof(STARTUP_TIME)); 228 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 229 | + sizeof(STARTUP_TIME); 230 | 231 | while (1) { 232 | ret = broadcast_msg(PC_SERVER_PORT, buf, 233 | sizeof(heartbeat_msg) + ((struct ipcam_search_msg *)buf)->exten_len); 234 | if (ret < 0) 235 | debug_log("broadcast_msg fail"); 236 | 237 | ((struct ipcam_search_msg *)buf)->heartbeat_num++; 238 | sleep(HEARTBEAT_CYCLE); 239 | } /* while (1) */ 240 | } 241 | 242 | static void release_exit(int signo) 243 | { 244 | send_logout_msg(); 245 | 246 | /* 247 | * release resources 248 | */ 249 | close(IPCAM_SERVER_FD); 250 | close(IPCAM_CLIENT_FD); 251 | close_debug_log(); 252 | 253 | /* 254 | * exit 255 | */ 256 | exit(signo); 257 | } 258 | 259 | int main(int argc, char **argv) 260 | { 261 | int ret; 262 | #if _LINUX_ 263 | int devnullfd; 264 | struct sigaction sa; 265 | #endif 266 | pthread_t deal_msg_pid; 267 | 268 | signal(SIGINT, release_exit); 269 | signal(SIGTERM, release_exit); 270 | open_debug_log("./_ipcam_device_debug.log"); 271 | 272 | STARTUP_TIME = time(NULL); 273 | get_mac(IPCAM_MAC); 274 | 275 | /* 276 | * deamon init 277 | */ 278 | ret = umask(0); 279 | if (ret == -1) { 280 | debug_print("umask fail"); 281 | exit(errno); 282 | } 283 | 284 | #if _LINUX_ 285 | devnullfd = open("/dev/null", 0); 286 | if (devnullfd == -1) { 287 | debug_print("open fail"); 288 | exit(errno); 289 | } 290 | 291 | ret = dup2(devnullfd, STDIN_FILENO); 292 | if (ret == -1) { 293 | debug_log("dup2 fail"); 294 | exit(errno); 295 | } 296 | 297 | ret = dup2(devnullfd, STDOUT_FILENO); 298 | if (ret == -1) { 299 | debug_log("dup2 fail"); 300 | exit(errno); 301 | } 302 | 303 | switch (fork()) { 304 | case -1: 305 | debug_log("fork fail"); 306 | exit(errno); 307 | break; 308 | case 0: 309 | break; 310 | default: 311 | exit(0); 312 | break; 313 | } 314 | setsid(); 315 | 316 | sa.sa_handler = SIG_IGN; 317 | sigemptyset(&sa.sa_mask); 318 | sa.sa_flags = 0; 319 | if (sigaction(SIGHUP, &sa, NULL) < 0) { 320 | debug_log("sigaction fail"); 321 | exit(errno); 322 | } 323 | #endif 324 | 325 | if (chdir("/") < 0) { 326 | debug_log("chdir fail"); 327 | exit(errno); 328 | } 329 | 330 | /* 331 | * server init 332 | */ 333 | SSRC = getpid(); 334 | initsocket(); 335 | broadcast_login_msg(); 336 | 337 | ret = pthread_create(&deal_msg_pid, 0, recv_msg_from_pc, NULL); 338 | if (ret) { 339 | debug_log("pthread_create failed"); 340 | exit(errno); 341 | } 342 | 343 | send_heartbeat_msg(); 344 | 345 | pthread_join(deal_msg_pid, NULL); 346 | close_debug_log(); 347 | 348 | return 0; 349 | } 350 | -------------------------------------------------------------------------------- /ipcam_search_devicev2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "ipcam_message.h" 14 | #include "socket_wrap.h" 15 | #include "get_mac.h" 16 | #include "config_ipcam_info.h" 17 | #include "para_parse.h" 18 | #include "debug_print.h" 19 | 20 | #include "ae.c" 21 | #include "anet.c" 22 | 23 | #define IPCAM_SERVER_PORT 6755 24 | #define PC_SERVER_PORT 6756 25 | #define MAX_MSG_LEN 512 26 | #define HEARTBEAT_CYCLE 10 /* 10s */ 27 | 28 | static int IPCAM_SERVER_FD = -1; 29 | static int IPCAM_CLIENT_FD = -1; 30 | static time_t STARTUP_TIME; 31 | static uint32_t SSRC; 32 | static uint8_t IPCAM_MAC[6]; 33 | 34 | int initsocket(void); 35 | static void replay_alive_msg(const struct ipcam_search_msg *recv_msg, const struct sockaddr_in *from); 36 | static void ipcam_deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from); 37 | static void recv_msg_from_pc(aeEventLoop *loop, int fd, void *privdata, int mask); 38 | void broadcast_login_msg(void); 39 | static int send_heartbeat_msg(struct aeEventLoop *loop, long long id, void *clientData); 40 | static void send_logout_msg(void); 41 | static void release_exit(int signo); 42 | 43 | int initsocket(void) 44 | { 45 | int ret; 46 | struct sockaddr_in ipcam_server; 47 | 48 | IPCAM_CLIENT_FD = socket(AF_INET, SOCK_DGRAM, 0); 49 | if (IPCAM_CLIENT_FD == -1) { 50 | debug_log("socket fail"); 51 | exit(errno); 52 | } 53 | 54 | IPCAM_SERVER_FD = socket(AF_INET, SOCK_DGRAM, 0); 55 | if (IPCAM_SERVER_FD == -1) { 56 | debug_log("socket fail"); 57 | exit(errno); 58 | } 59 | 60 | ipcam_server.sin_family = AF_INET; 61 | ipcam_server.sin_port = htons(IPCAM_SERVER_PORT); 62 | ipcam_server.sin_addr.s_addr = htonl(INADDR_ANY); 63 | 64 | ret = bind(IPCAM_SERVER_FD, (struct sockaddr*)&ipcam_server, 65 | sizeof(ipcam_server)); 66 | if (ret == -1) { 67 | debug_log("bind fail"); 68 | exit(errno); 69 | } 70 | 71 | return 0; 72 | } /* int initsocket(void) */ 73 | 74 | static void replay_alive_msg(const struct ipcam_search_msg *recv_msg, const struct sockaddr_in *from) 75 | { 76 | struct sockaddr_in remote_sockaddr; 77 | struct ipcam_search_msg send_msg = {0}; 78 | uint8_t buf[MAX_MSG_LEN]; 79 | 80 | send_msg.type = IPCAMMSG_ACK_ALIVE; 81 | send_msg.deal_id = recv_msg->deal_id; 82 | send_msg.ssrc = SSRC; 83 | send_msg.timestamp = time(NULL); 84 | get_ipcam_name(send_msg.ipcam_name); 85 | memcpy(buf, &send_msg, sizeof(send_msg)); 86 | memcpy(buf + sizeof(send_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 87 | memcpy(buf + sizeof(send_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 88 | sizeof(STARTUP_TIME)); 89 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 90 | + sizeof(STARTUP_TIME); 91 | 92 | memcpy(&remote_sockaddr, from, sizeof(remote_sockaddr)); 93 | remote_sockaddr.sin_port = htons(PC_SERVER_PORT); 94 | send_msg_by_sockaddr(buf, sizeof(send_msg) + 95 | ((struct ipcam_search_msg *)buf)->exten_len, &remote_sockaddr); 96 | 97 | return; 98 | } 99 | 100 | static void ipcam_deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from) 101 | { 102 | switch (msg->type) { 103 | case IPCAMMSG_QUERY_ALIVE: 104 | replay_alive_msg(msg, from); 105 | break; 106 | default: 107 | break; 108 | } 109 | 110 | return; 111 | } 112 | 113 | static void recv_msg_from_pc(aeEventLoop *loop, int fd, void *privdata, int mask) 114 | { 115 | int ret; 116 | struct sockaddr_in peer; 117 | uint8_t buf[MAX_MSG_LEN]; 118 | struct ipcam_search_msg *msg_buf; 119 | socklen_t len; 120 | 121 | len = sizeof(struct sockaddr_in); 122 | ret = recvfrom(IPCAM_SERVER_FD, buf, sizeof(buf), 0, 123 | (struct sockaddr *)&peer, 124 | (socklen_t *)&len); 125 | if (ret < 0) { 126 | debug_log("recvfrom fail"); 127 | return; 128 | } 129 | msg_buf = malloc(sizeof(struct ipcam_search_msg) 130 | + ((struct ipcam_search_msg *)buf)->exten_len); 131 | parse_msg((const char *)buf, ret, msg_buf); 132 | ipcam_deal_msg_func(msg_buf, &peer); 133 | 134 | if (msg_buf) { 135 | free(msg_buf); 136 | msg_buf = NULL; 137 | } 138 | } 139 | 140 | void broadcast_login_msg(void) 141 | { 142 | int ret; 143 | struct ipcam_search_msg login_msg; 144 | uint8_t buf[MAX_MSG_LEN]; 145 | 146 | debug_log("mac is %02x:%02x:%02x:%02x:%02x:%02x", 147 | IPCAM_MAC[0], 148 | IPCAM_MAC[1], 149 | IPCAM_MAC[2], 150 | IPCAM_MAC[3], 151 | IPCAM_MAC[4], 152 | IPCAM_MAC[5]); 153 | 154 | memset(&login_msg, 0, sizeof(login_msg)); 155 | login_msg.type = IPCAMMSG_LOGIN; 156 | login_msg.ssrc = SSRC; 157 | login_msg.timestamp = time(NULL); 158 | get_ipcam_name(login_msg.ipcam_name); 159 | 160 | memcpy(buf, &login_msg, sizeof(login_msg)); 161 | memcpy(buf + sizeof(login_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 162 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC); 163 | ret = broadcast_msg(PC_SERVER_PORT, buf, sizeof(login_msg) + sizeof(IPCAM_MAC)); 164 | debug_log("sendto return %d", ret); 165 | 166 | return; 167 | } 168 | 169 | static void send_logout_msg(void) 170 | { 171 | int ret; 172 | struct ipcam_search_msg logout_msg; 173 | uint8_t buf[MAX_MSG_LEN]; 174 | 175 | memset(&logout_msg, 0, sizeof(logout_msg)); 176 | 177 | logout_msg.type = IPCAMMSG_LOGOUT; 178 | logout_msg.ssrc = SSRC; 179 | logout_msg.timestamp = time(NULL); 180 | get_ipcam_name(logout_msg.ipcam_name); 181 | 182 | memcpy(buf, &logout_msg, sizeof(logout_msg)); 183 | memcpy(buf + sizeof(logout_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 184 | 185 | memcpy(buf + sizeof(logout_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 186 | sizeof(STARTUP_TIME)); 187 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 188 | + sizeof(STARTUP_TIME); 189 | 190 | ret = broadcast_msg(PC_SERVER_PORT, buf, 191 | sizeof(logout_msg) + ((struct ipcam_search_msg *)buf)->exten_len); 192 | if (ret < 0) 193 | debug_log("broadcast_msg fail"); 194 | } 195 | 196 | static int send_heartbeat_msg(struct aeEventLoop *loop, long long id, void *clientData) 197 | { 198 | int ret; 199 | 200 | ret = broadcast_msg(PC_SERVER_PORT, clientData, 201 | sizeof(struct ipcam_search_msg) + ((struct ipcam_search_msg *)clientData)->exten_len); 202 | if (ret < 0) 203 | debug_log("broadcast_msg fail"); 204 | 205 | ((struct ipcam_search_msg *)clientData)->heartbeat_num++; 206 | return HEARTBEAT_CYCLE * 1000; 207 | } 208 | 209 | static void release_exit(int signo) 210 | { 211 | send_logout_msg(); 212 | 213 | /* 214 | * release resources 215 | */ 216 | close(IPCAM_SERVER_FD); 217 | close(IPCAM_CLIENT_FD); 218 | close_debug_log(); 219 | 220 | /* 221 | * exit 222 | */ 223 | exit(signo); 224 | } 225 | 226 | int main(int argc, char **argv) 227 | { 228 | int ret; 229 | aeEventLoop *loop; 230 | #if _LINUX_ 231 | int devnullfd; 232 | struct sigaction sa; 233 | #endif 234 | 235 | signal(SIGINT, release_exit); 236 | signal(SIGTERM, release_exit); 237 | open_debug_log("./_ipcam_device_debug.log"); 238 | 239 | STARTUP_TIME = time(NULL); 240 | get_mac(IPCAM_MAC); 241 | 242 | /* 243 | * deamon init 244 | */ 245 | ret = umask(0); 246 | if (ret == -1) { 247 | debug_print("umask fail"); 248 | exit(errno); 249 | } 250 | 251 | #if _LINUX_ 252 | devnullfd = open("/dev/null", 0); 253 | if (devnullfd == -1) { 254 | debug_print("open fail"); 255 | exit(errno); 256 | } 257 | 258 | ret = dup2(devnullfd, STDIN_FILENO); 259 | if (ret == -1) { 260 | debug_log("dup2 fail"); 261 | exit(errno); 262 | } 263 | 264 | ret = dup2(devnullfd, STDOUT_FILENO); 265 | if (ret == -1) { 266 | debug_log("dup2 fail"); 267 | exit(errno); 268 | } 269 | 270 | switch (fork()) { 271 | case -1: 272 | debug_log("fork fail"); 273 | exit(errno); 274 | break; 275 | case 0: 276 | break; 277 | default: 278 | exit(0); 279 | break; 280 | } 281 | setsid(); 282 | 283 | sa.sa_handler = SIG_IGN; 284 | sigemptyset(&sa.sa_mask); 285 | sa.sa_flags = 0; 286 | if (sigaction(SIGHUP, &sa, NULL) < 0) { 287 | debug_log("sigaction fail"); 288 | exit(errno); 289 | } 290 | #endif 291 | 292 | if (chdir("/") < 0) { 293 | debug_log("chdir fail"); 294 | exit(errno); 295 | } 296 | 297 | /* 298 | * server init 299 | */ 300 | SSRC = getpid(); 301 | initsocket(); 302 | broadcast_login_msg(); 303 | 304 | uint8_t buf[MAX_MSG_LEN]; 305 | struct ipcam_search_msg heartbeat_msg; 306 | 307 | memset(&heartbeat_msg, 0, sizeof(heartbeat_msg)); 308 | 309 | heartbeat_msg.type = IPCAMMSG_HEARTBEAT; 310 | heartbeat_msg.ssrc = SSRC; 311 | heartbeat_msg.timestamp = time(NULL); 312 | heartbeat_msg.heartbeat_num = 0; 313 | get_ipcam_name(heartbeat_msg.ipcam_name); 314 | 315 | memcpy(buf, &heartbeat_msg, sizeof(heartbeat_msg)); 316 | memcpy(buf + sizeof(heartbeat_msg), IPCAM_MAC, sizeof(IPCAM_MAC)); 317 | memcpy(buf + sizeof(heartbeat_msg) + sizeof(IPCAM_MAC), &STARTUP_TIME, 318 | sizeof(STARTUP_TIME)); 319 | ((struct ipcam_search_msg *)buf)->exten_len = sizeof(IPCAM_MAC) 320 | + sizeof(STARTUP_TIME); 321 | 322 | 323 | loop = aeCreateEventLoop(); 324 | aeCreateFileEvent(loop, IPCAM_SERVER_FD, AE_READABLE, recv_msg_from_pc, NULL); 325 | aeCreateTimeEvent(loop, HEARTBEAT_CYCLE * 1000, send_heartbeat_msg, (void *)buf, NULL); 326 | aeMain(loop); 327 | 328 | close_debug_log(); 329 | 330 | return 0; 331 | } 332 | -------------------------------------------------------------------------------- /ipcam_search_pc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #if _LINUX_ 9 | #include 10 | #include 11 | #include 12 | #else 13 | #include 14 | #define sleep(n) Sleep(1000 * (n)) 15 | #endif 16 | #include 17 | #include 18 | #include "socket_wrap.h" 19 | #include "ipcam_message.h" 20 | #include "ipcam_list.h" 21 | #include "para_parse.h" 22 | #include "debug_print.h" 23 | 24 | #if _LINUX_ 25 | #include "ae.c" 26 | #include "anet.c" 27 | #endif 28 | 29 | #ifndef LINE_MAX 30 | #define LINE_MAX 2048 31 | #endif 32 | 33 | #define MAX_MSG_LEN 512 34 | #define IPCAM_SERVER_PORT 6755 35 | #define PC_SERVER_PORT 6756 36 | #define CHECK_IPCAM_CYCLE 30 /* 30s */ 37 | 38 | static ipcam_link IPCAM_DEV; 39 | static uint32_t SSRC; 40 | 41 | static pthread_mutex_t IPCAM_DEV_MUTEX = PTHREAD_MUTEX_INITIALIZER; 42 | 43 | void list_ipcam(ipcam_link ipcam_dev); 44 | int run_cmd_by_string(char *cmd_string); 45 | static void search_ipcam(void); 46 | static char *get_line(char *s, size_t n, FILE *f); 47 | void ctrl_c(int signo); 48 | void deal_console_input_sig_init(void); 49 | void release_exit(int signo); 50 | void *deal_console_input(void *p); 51 | void *recv_msg_from_ipcam(void *p); 52 | static void clear_all_dev_online(ipcam_link IPCAM_DEV); 53 | static void test_all_dev_online(ipcam_link IPCAM_DEV); 54 | void *maintain_ipcam_link(void *p); 55 | static void deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from); 56 | #if 0 57 | struct ipcam_search_msg { 58 | uint8_t type; 59 | uint8_t deal_id; 60 | uint8_t flag; 61 | uint8_t exten_len; 62 | uint32_t ssrc; 63 | uint32_t timestamp; 64 | uint32_t heartbeat_num; 65 | char ipcam_name[64]; 66 | char exten_msg[0]; 67 | } __attribute ((packed)); 68 | 69 | enum { 70 | IPCAMMSG_LOGIN = 0x1, 71 | IPCAMMSG_LOGOUT, 72 | IPCAMMSG_HEARTBEAT, 73 | IPCAMMSG_QUERY_ALIVE, 74 | IPCAMMSG_ACK_ALIVE, 75 | IPCAMMSG_DHCP, 76 | IPCAMMSG_ACK_DHCP, 77 | IPCAMMSG_SET_IP, 78 | IPCAMMSG_ACK_IP, 79 | } ipcam_search_msg_type; 80 | #endif 81 | 82 | void list_ipcam(ipcam_link ipcam_dev) 83 | { 84 | time_t lt; 85 | char time_str[128] = {0}; 86 | pipcam_node q = ipcam_dev->next; 87 | 88 | #if _LINUX_ 89 | fprintf(stdout, "\033[01;33mALIVE?\tIP ADDR\t\tDEV NAME\t\tMAC\t\tSTARTUP TIME\n\033[0m"); 90 | #else 91 | fprintf(stdout, "ALIVE?\tIP ADDR\t\tDEV NAME\t\tMAC\t\tSTARTUP TIME\n"); 92 | #endif 93 | while (q) { 94 | if (q->alive_flag & 1) 95 | printf("yes\t"); 96 | else 97 | printf("no\t"); 98 | 99 | printf("%s\t", inet_ntoa(q->node_info.ipaddr)); 100 | printf("%s\t", q->node_info.ipcam_name); 101 | printf("%02x:%02x:%02x:%02x:%02x:%02x\t", 102 | q->node_info.mac[0], 103 | q->node_info.mac[1], 104 | q->node_info.mac[2], 105 | q->node_info.mac[3], 106 | q->node_info.mac[4], 107 | q->node_info.mac[5]); 108 | lt = q->node_info.startup_time; 109 | strftime(time_str, sizeof(time_str), "%F %R:%S", 110 | localtime(<)); 111 | 112 | printf("%s\n", time_str); 113 | q = q->next; 114 | } 115 | return; 116 | } 117 | 118 | int run_cmd_by_string(char *cmd_string) 119 | { 120 | int ret = -1; 121 | 122 | switch (cmd_string[0]) { 123 | case 'r': /* renew ipcam list */ 124 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 125 | delete_ipcam_all_node(IPCAM_DEV); 126 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 127 | case 's': /* search ipcam dev */ 128 | search_ipcam(); 129 | sleep(1); 130 | case 'l': /* list ipcam dev */ 131 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 132 | list_ipcam(IPCAM_DEV); 133 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 134 | break; 135 | case 'q': 136 | release_exit(0); 137 | break; 138 | case 'h': 139 | default: 140 | printf("\'s\': search ipcam dev\n"); 141 | printf("\'r\': renew ipcam list\n"); 142 | printf("\'l\': list all ipcam dev\n"); 143 | printf("\'q\': quit\n"); 144 | printf("setipcname?ip=ip&name=name\n"); 145 | printf("\'h\': show this help\n"); 146 | break; 147 | } /* switch (cmd_string[0]) */ 148 | 149 | return ret; 150 | } 151 | 152 | static void search_ipcam(void) 153 | { 154 | int ret; 155 | struct ipcam_search_msg send_msg; 156 | 157 | memset(&send_msg, 0, sizeof(send_msg)); 158 | send_msg.type = 0x4; 159 | send_msg.ssrc = SSRC; 160 | ret = broadcast_msg(IPCAM_SERVER_PORT, &send_msg, sizeof(send_msg)); 161 | if (ret < 0) 162 | debug_print("fail: sendto return %d", ret); 163 | 164 | return; 165 | } 166 | 167 | static char *get_line(char *s, size_t n, FILE *f) 168 | { 169 | char *p = fgets(s, n, f); 170 | 171 | if (p) { 172 | size_t last = strlen(s) - 1; 173 | 174 | if (s[last] == '\n') 175 | s[last] = '\0'; 176 | } 177 | return p; 178 | } 179 | 180 | void ctrl_c(int signo) 181 | { 182 | fprintf(stdout, "\nGood-bye \n"); 183 | release_exit(0); 184 | return; 185 | } 186 | 187 | void release_exit(int signo) 188 | { 189 | /* 190 | * release resources 191 | */ 192 | free_ipcam_link(IPCAM_DEV); 193 | 194 | /* 195 | * exit 196 | */ 197 | exit(signo); 198 | } 199 | 200 | void deal_console_input_sig_init(void) 201 | { 202 | signal(SIGINT, ctrl_c); 203 | return; 204 | } 205 | 206 | void *deal_console_input(void *p) 207 | { 208 | char line_buf[LINE_MAX] = {0}; 209 | char *curr_cmd = NULL; 210 | int ret; 211 | 212 | deal_console_input_sig_init(); 213 | fprintf(stdout, "Hello, This is ipc_shell\n" 214 | "type \"help\" for help\n"); 215 | #if _LINUX_ 216 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 217 | #else 218 | fprintf(stdout, "ipc_shell> "); 219 | #endif 220 | while (get_line(line_buf, sizeof(line_buf), stdin)) { 221 | curr_cmd = strtok(line_buf, ";"); 222 | if (curr_cmd) { 223 | ret = run_cmd_by_string(curr_cmd); 224 | } 225 | 226 | while ((curr_cmd = strtok(NULL, ";")) != NULL) { 227 | ret = run_cmd_by_string(curr_cmd); 228 | debug_print("run_cmd_by_string return %d", ret); 229 | } 230 | #if _LINUX_ 231 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 232 | #else 233 | fprintf(stdout, "ipc_shell> "); 234 | #endif 235 | } /* while (get_line(line_buf, sizeof(line_buf), stdin)) */ 236 | fprintf(stdout, "Good-bye \n"); 237 | release_exit(0); 238 | return NULL; 239 | } /* void *deal_console_input(void *p) */ 240 | 241 | static void deal_msg_func(const struct ipcam_search_msg *msg, 242 | const struct sockaddr_in *from) 243 | { 244 | int ret; 245 | ipcam_info_t remote_ipcam_info; 246 | struct ipcam_node new_ipcam_node; 247 | pipcam_node tmp_node; 248 | 249 | memset(&remote_ipcam_info, 0, sizeof(remote_ipcam_info)); 250 | 251 | switch (msg->type) { 252 | case IPCAMMSG_LOGIN: 253 | debug_print("new ipcam login"); 254 | memcpy(&remote_ipcam_info.ipaddr, &from->sin_addr, 255 | (size_t)sizeof(struct in_addr)); 256 | memcpy(remote_ipcam_info.mac, msg->exten_msg, msg->exten_len); 257 | memcpy(remote_ipcam_info.ipcam_name, msg->ipcam_name, 258 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 259 | remote_ipcam_info.startup_time = msg->timestamp; 260 | debug_print("ipaddr is %s", inet_ntoa(remote_ipcam_info.ipaddr)); 261 | debug_print("mac is %02x:%02x:%02x:%02x:%02x:%02x", 262 | remote_ipcam_info.mac[0], 263 | remote_ipcam_info.mac[1], 264 | remote_ipcam_info.mac[2], 265 | remote_ipcam_info.mac[3], 266 | remote_ipcam_info.mac[4], 267 | remote_ipcam_info.mac[5]); 268 | debug_print("ipcam_name is %s\n", remote_ipcam_info.ipcam_name); 269 | debug_print("timestamp is %d\n", remote_ipcam_info.startup_time); 270 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 271 | new_ipcam_node.node_info = remote_ipcam_info; 272 | new_ipcam_node.alive_flag = 1; 273 | 274 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 275 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 276 | if (!ret) { 277 | tmp_node 278 | = search_ipcam_node_by_mac(IPCAM_DEV, 279 | remote_ipcam_info.mac); 280 | tmp_node->alive_flag = 1; 281 | } 282 | debug_print("insert return %d", ret); 283 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 284 | 285 | break; 286 | case IPCAMMSG_LOGOUT: 287 | debug_print("recv IPCAMMSG_LOGOUT msg"); 288 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 289 | ret = delete_ipcam_node_by_mac(IPCAM_DEV, msg->exten_msg); 290 | if (!ret) 291 | debug_print("it work badly"); 292 | 293 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 294 | break; 295 | case IPCAMMSG_HEARTBEAT: 296 | memcpy(&remote_ipcam_info.ipaddr, 297 | &from->sin_addr, 298 | (size_t)sizeof(struct in_addr)); 299 | memcpy(remote_ipcam_info.mac, 300 | msg->exten_msg, 301 | sizeof(remote_ipcam_info.mac)); 302 | memcpy(remote_ipcam_info.ipcam_name, 303 | msg->ipcam_name, 304 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 305 | memcpy(&remote_ipcam_info.startup_time, 306 | msg->exten_msg + sizeof(remote_ipcam_info.mac), 307 | sizeof(remote_ipcam_info.startup_time)); 308 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 309 | new_ipcam_node.node_info = remote_ipcam_info; 310 | new_ipcam_node.alive_flag = 1; 311 | 312 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 313 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 314 | if (!ret) { 315 | tmp_node = search_ipcam_node_by_mac(IPCAM_DEV, 316 | remote_ipcam_info.mac); 317 | tmp_node->alive_flag = 1; 318 | } 319 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 320 | 321 | break; 322 | case IPCAMMSG_ACK_ALIVE: 323 | memcpy(&remote_ipcam_info.ipaddr, &from->sin_addr, 324 | (size_t)sizeof(struct in_addr)); 325 | memcpy(remote_ipcam_info.mac, msg->exten_msg, 326 | sizeof(remote_ipcam_info.mac)); 327 | memcpy(remote_ipcam_info.ipcam_name, msg->ipcam_name, 328 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 329 | memcpy(&remote_ipcam_info.startup_time, 330 | msg->exten_msg + sizeof(remote_ipcam_info.mac), 331 | sizeof(remote_ipcam_info.startup_time)); 332 | debug_print("ipaddr is %s", inet_ntoa(remote_ipcam_info.ipaddr)); 333 | debug_print("mac is %02x:%02x:%02x:%02x:%02x:%02x", 334 | remote_ipcam_info.mac[0], 335 | remote_ipcam_info.mac[1], 336 | remote_ipcam_info.mac[2], 337 | remote_ipcam_info.mac[3], 338 | remote_ipcam_info.mac[4], 339 | remote_ipcam_info.mac[5]); 340 | debug_print("ipcam_name is %s\n", remote_ipcam_info.ipcam_name); 341 | debug_print("timestamp is %d\n", remote_ipcam_info.startup_time); 342 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 343 | new_ipcam_node.node_info = remote_ipcam_info; 344 | new_ipcam_node.alive_flag = 1; 345 | 346 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 347 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 348 | if (!ret) { 349 | tmp_node 350 | = search_ipcam_node_by_mac(IPCAM_DEV, 351 | remote_ipcam_info.mac); 352 | tmp_node->alive_flag = 1; 353 | } 354 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 355 | 356 | break; 357 | case IPCAMMSG_ACK_DHCP: 358 | break; 359 | case IPCAMMSG_ACK_IP: 360 | break; 361 | default: 362 | debug_print("i am confused by the type of %d ", msg->type); 363 | } /* switch (msg->type) */ 364 | 365 | return; 366 | } /* static void deal_msg_func() */ 367 | 368 | void *recv_msg_from_ipcam(void *p) 369 | { 370 | int pc_server_fd; 371 | struct sockaddr_in pc_server; 372 | struct sockaddr_in peer; 373 | char buf[MAX_MSG_LEN]; 374 | struct ipcam_search_msg *msg_buf; 375 | socklen_t len; 376 | int ret; 377 | int sock_opt; 378 | 379 | #if !_LINUX_ 380 | WSADATA wsadata; 381 | if (WSAStartup(MAKEWORD(1, 1), &wsadata) == SOCKET_ERROR) { 382 | debug_print("WSAStartup() fail\n"); 383 | exit(errno); 384 | } 385 | #endif 386 | 387 | pc_server_fd = socket(AF_INET, SOCK_DGRAM, 0); 388 | if (pc_server_fd == -1) { 389 | debug_print("socket fail"); 390 | exit(errno); 391 | } 392 | 393 | sock_opt = 1; 394 | ret = setsockopt(pc_server_fd, SOL_SOCKET, SO_REUSEADDR, 395 | &sock_opt, sizeof(sock_opt)); 396 | if (ret < 0) { 397 | debug_print("setsockopt fail"); 398 | exit(errno); 399 | } 400 | 401 | pc_server.sin_family = AF_INET; 402 | pc_server.sin_port = htons(PC_SERVER_PORT); 403 | pc_server.sin_addr.s_addr = htonl(INADDR_ANY); 404 | 405 | ret = bind(pc_server_fd, (struct sockaddr*)&pc_server, 406 | sizeof(pc_server)); 407 | if (ret == -1) { 408 | debug_print("bind fail"); 409 | exit(errno); 410 | } 411 | 412 | while (1) { 413 | len = sizeof(struct sockaddr_in); 414 | ret = recvfrom(pc_server_fd, buf, sizeof(buf), 0, 415 | (struct sockaddr *)&peer, 416 | (socklen_t *)&len); 417 | if (ret < 0) { 418 | debug_print("recvfrom fail"); 419 | continue; 420 | } 421 | msg_buf = malloc(sizeof(struct ipcam_search_msg) 422 | + ((struct ipcam_search_msg *)buf)->exten_len); 423 | parse_msg(buf, ret, msg_buf); 424 | deal_msg_func(msg_buf, &peer); 425 | 426 | if (msg_buf) { 427 | free(msg_buf); 428 | msg_buf = NULL; 429 | } 430 | } /* while (1) */ 431 | 432 | return NULL; 433 | } /* void *recv_msg_from_ipcam(void *p) */ 434 | 435 | static void clear_all_dev_online(ipcam_link IPCAM_DEV) 436 | { 437 | pipcam_node q = IPCAM_DEV->next; 438 | 439 | while (q) { 440 | q->alive_flag |= 1 << 1; 441 | q = q->next; 442 | } 443 | } 444 | 445 | static void test_all_dev_online(ipcam_link IPCAM_DEV) 446 | { 447 | pipcam_node q = IPCAM_DEV->next; 448 | 449 | while (q) { 450 | if (q->alive_flag != 1) 451 | q->alive_flag = 0; 452 | q = q->next; 453 | } 454 | } 455 | 456 | void *maintain_ipcam_link(void *p) 457 | { 458 | while (1) { 459 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 460 | clear_all_dev_online(IPCAM_DEV); 461 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 462 | 463 | sleep(CHECK_IPCAM_CYCLE); 464 | 465 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 466 | test_all_dev_online(IPCAM_DEV); 467 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 468 | } 469 | 470 | return NULL; 471 | } 472 | 473 | static int watch_ipcam_link_clear(struct aeEventLoop *loop, long long id, void *clientData); 474 | static int watch_ipcam_link_test(struct aeEventLoop *loop, long long id, void *clientData); 475 | 476 | static int watch_ipcam_link_clear(struct aeEventLoop *loop, long long id, void *clientData) 477 | { 478 | debug_print("clear"); 479 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 480 | clear_all_dev_online(IPCAM_DEV); 481 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 482 | aeCreateTimeEvent(loop, CHECK_IPCAM_CYCLE * 1000, watch_ipcam_link_test, NULL, NULL); 483 | return -1; 484 | } 485 | 486 | static int watch_ipcam_link_test(struct aeEventLoop *loop, long long id, void *clientData) 487 | { 488 | debug_print("test"); 489 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 490 | test_all_dev_online(IPCAM_DEV); 491 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 492 | 493 | pthread_mutex_lock(&IPCAM_DEV_MUTEX); 494 | clear_all_dev_online(IPCAM_DEV); 495 | pthread_mutex_unlock(&IPCAM_DEV_MUTEX); 496 | 497 | return CHECK_IPCAM_CYCLE * 1000; 498 | } 499 | 500 | static void dealcmd(aeEventLoop *loop, int fd, void *privdata, int mask) 501 | { 502 | char line_buf[LINE_MAX] = {0}; 503 | char *curr_cmd = NULL; 504 | int ret; 505 | 506 | get_line(line_buf, sizeof(line_buf), stdin); 507 | curr_cmd = strtok(line_buf, ";"); 508 | if (curr_cmd) { 509 | ret = run_cmd_by_string(curr_cmd); 510 | } 511 | 512 | while ((curr_cmd = strtok(NULL, ";")) != NULL) { 513 | ret = run_cmd_by_string(curr_cmd); 514 | debug_print("run_cmd_by_string return %d", ret); 515 | } 516 | #if _LINUX_ 517 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 518 | #else 519 | fprintf(stdout, "ipc_shell> "); 520 | #endif 521 | fflush(stdout); 522 | } 523 | 524 | static int init_server_UDP_fd(int port, char *bindaddr) 525 | { 526 | int pc_server_fd; 527 | struct sockaddr_in pc_server; 528 | int ret; 529 | int sock_opt; 530 | 531 | #if !_LINUX_ 532 | WSADATA wsadata; 533 | if (WSAStartup(MAKEWORD(1, 1), &wsadata) == SOCKET_ERROR) { 534 | debug_print("WSAStartup() fail\n"); 535 | exit(errno); 536 | } 537 | #endif 538 | 539 | pc_server_fd = socket(AF_INET, SOCK_DGRAM, 0); 540 | if (pc_server_fd == -1) { 541 | debug_print("socket fail"); 542 | exit(errno); 543 | } 544 | 545 | sock_opt = 1; 546 | ret = setsockopt(pc_server_fd, SOL_SOCKET, SO_REUSEADDR, 547 | &sock_opt, sizeof(sock_opt)); 548 | if (ret < 0) { 549 | debug_print("setsockopt fail"); 550 | exit(errno); 551 | } 552 | 553 | pc_server.sin_family = AF_INET; 554 | pc_server.sin_port = htons(port); 555 | pc_server.sin_addr.s_addr = htonl(INADDR_ANY); 556 | if (bindaddr && inet_aton(bindaddr, &pc_server.sin_addr) == 0) { 557 | debug_print("invalid bind address"); 558 | close(pc_server_fd); 559 | return -1; 560 | } 561 | 562 | ret = bind(pc_server_fd, (struct sockaddr*)&pc_server, 563 | sizeof(pc_server)); 564 | if (ret == -1) { 565 | debug_print("bind fail"); 566 | exit(errno); 567 | } 568 | return pc_server_fd; 569 | } 570 | 571 | static void dealnet(aeEventLoop *loop, int fd, void *privdata, int mask) 572 | { 573 | struct sockaddr_in peer; 574 | char buf[MAX_MSG_LEN]; 575 | struct ipcam_search_msg *msg_buf; 576 | socklen_t len; 577 | int ret; 578 | 579 | len = sizeof(struct sockaddr_in); 580 | ret = recvfrom(fd, buf, sizeof(buf), 0, 581 | (struct sockaddr *)&peer, (socklen_t *)&len); 582 | if (ret < 0) { 583 | debug_print("recvfrom fail"); 584 | return; 585 | } 586 | msg_buf = malloc(sizeof(struct ipcam_search_msg) 587 | + ((struct ipcam_search_msg *)buf)->exten_len); 588 | parse_msg(buf, ret, msg_buf); 589 | deal_msg_func(msg_buf, &peer); 590 | 591 | if (msg_buf) { 592 | free(msg_buf); 593 | msg_buf = NULL; 594 | } 595 | } 596 | 597 | int main(int argc, char **argv) 598 | { 599 | int ret; 600 | #if _LINUX_ 601 | aeEventLoop *loop; 602 | #endif 603 | 604 | SSRC = getpid(); 605 | IPCAM_DEV = create_empty_ipcam_link(); 606 | 607 | #if !_LINUX_ 608 | pthread_t deal_msg_pid; 609 | pthread_t deal_console_input_pid; 610 | pthread_t maintain_ipcam_link_pid; 611 | 612 | ret = pthread_create(&deal_console_input_pid, 0, 613 | deal_console_input, NULL); 614 | if (ret) { 615 | debug_print("pthread_create failed"); 616 | exit(errno); 617 | } 618 | 619 | ret = pthread_create(&deal_msg_pid, 0, 620 | recv_msg_from_ipcam, NULL); 621 | if (ret) { 622 | debug_print("pthread_create failed"); 623 | exit(errno); 624 | } 625 | 626 | ret = pthread_create(&maintain_ipcam_link_pid, 0, 627 | maintain_ipcam_link, NULL); 628 | if (ret) { 629 | debug_print("pthread_create failed"); 630 | exit(errno); 631 | } 632 | 633 | pthread_join(maintain_ipcam_link_pid, NULL); 634 | pthread_join(deal_msg_pid, NULL); 635 | pthread_join(deal_console_input_pid, NULL); 636 | #else 637 | int pc_server_fd; 638 | 639 | pc_server_fd = init_server_UDP_fd(PC_SERVER_PORT, "0.0.0.0"); 640 | assert(pc_server_fd > 0); 641 | loop = aeCreateEventLoop(); 642 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 643 | fflush(stdout); 644 | ret = aeCreateFileEvent(loop, STDIN_FILENO, AE_READABLE, dealcmd, NULL); 645 | assert(ret != AE_ERR); 646 | ret = aeCreateFileEvent(loop, pc_server_fd, AE_READABLE, dealnet, NULL); 647 | assert(ret != AE_ERR); 648 | aeCreateTimeEvent(loop, CHECK_IPCAM_CYCLE * 1000, watch_ipcam_link_clear, NULL, NULL); 649 | aeMain(loop); 650 | #endif 651 | return 0; 652 | } 653 | -------------------------------------------------------------------------------- /ipcam_search_pcv2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "socket_wrap.h" 13 | #include "ipcam_message.h" 14 | #include "ipcam_list.h" 15 | #include "para_parse.h" 16 | #include "already_running.h" 17 | #include "debug_print.h" 18 | 19 | #include "ae.c" 20 | #include "anet.c" 21 | 22 | #ifndef LINE_MAX 23 | #define LINE_MAX 2048 24 | #endif 25 | 26 | #define MAX_MSG_LEN 512 27 | #define IPCAM_SERVER_PORT 6755 28 | #define PC_SERVER_PORT 6756 29 | #define CHECK_IPCAM_CYCLE 30 /* 30s */ 30 | 31 | static ipcam_link IPCAM_DEV; 32 | static uint32_t SSRC; 33 | 34 | static void dealcmd(aeEventLoop *loop, int fd, void *privdata, int mask); 35 | static void list_ipcam(ipcam_link ipcam_dev); 36 | static int run_cmd_by_string(aeEventLoop *loop, char *cmd_string); 37 | static void search_ipcam(void); 38 | static char *get_line(char *s, size_t n, FILE *f); 39 | static void release_exit(int signo); 40 | static void clear_all_dev_online(ipcam_link IPCAM_DEV); 41 | static void test_all_dev_online(ipcam_link IPCAM_DEV); 42 | static void deal_msg_func(const struct ipcam_search_msg *msg, const struct sockaddr_in *from); 43 | static int watch_ipcam_link_clear(struct aeEventLoop *loop, long long id, void *clientData); 44 | static int watch_ipcam_link_test(struct aeEventLoop *loop, long long id, void *clientData); 45 | 46 | static int callback_list_ipcam(struct aeEventLoop *loop, long long id, void *clientData); 47 | #if 0 48 | struct ipcam_search_msg { 49 | uint8_t type; 50 | uint8_t deal_id; 51 | uint8_t flag; 52 | uint8_t exten_len; 53 | uint32_t ssrc; 54 | uint32_t timestamp; 55 | uint32_t heartbeat_num; 56 | char ipcam_name[64]; 57 | char exten_msg[0]; 58 | } __attribute ((packed)); 59 | 60 | enum { 61 | IPCAMMSG_LOGIN = 0x1, 62 | IPCAMMSG_LOGOUT, 63 | IPCAMMSG_HEARTBEAT, 64 | IPCAMMSG_QUERY_ALIVE, 65 | IPCAMMSG_ACK_ALIVE, 66 | IPCAMMSG_DHCP, 67 | IPCAMMSG_ACK_DHCP, 68 | IPCAMMSG_SET_IP, 69 | IPCAMMSG_ACK_IP, 70 | } ipcam_search_msg_type; 71 | #endif 72 | 73 | static void list_ipcam(ipcam_link ipcam_dev) 74 | { 75 | time_t lt; 76 | char time_str[128] = {0}; 77 | pipcam_node q = ipcam_dev->next; 78 | 79 | fprintf(stdout, "\033[01;33mALIVE?\tIP ADDR\t\tDEV NAME\t\tMAC\t\tSTARTUP TIME\n\033[0m"); 80 | while (q) { 81 | if (q->alive_flag & 1) 82 | printf("yes\t"); 83 | else 84 | printf("no\t"); 85 | 86 | printf("%s\t", inet_ntoa(q->node_info.ipaddr)); 87 | printf("%s\t", q->node_info.ipcam_name); 88 | printf("%02x:%02x:%02x:%02x:%02x:%02x\t", 89 | q->node_info.mac[0], 90 | q->node_info.mac[1], 91 | q->node_info.mac[2], 92 | q->node_info.mac[3], 93 | q->node_info.mac[4], 94 | q->node_info.mac[5]); 95 | lt = q->node_info.startup_time; 96 | strftime(time_str, sizeof(time_str), "%F %R:%S", 97 | localtime(<)); 98 | 99 | printf("%s\n", time_str); 100 | q = q->next; 101 | } 102 | return; 103 | } 104 | 105 | static int run_cmd_by_string(aeEventLoop *loop, char *cmd_string) 106 | { 107 | int ret = -1; 108 | 109 | switch (cmd_string[0]) { 110 | case 'r': /* renew ipcam list */ 111 | delete_ipcam_all_node(IPCAM_DEV); 112 | case 's': /* search ipcam dev */ 113 | search_ipcam(); 114 | // sleep(1); 115 | aeCreateTimeEvent(loop, 1 * 150, callback_list_ipcam, NULL, NULL); 116 | break; 117 | case 'l': /* list ipcam dev */ 118 | list_ipcam(IPCAM_DEV); 119 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 120 | fflush(stdout); 121 | break; 122 | case 'q': 123 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 124 | fflush(stdout); 125 | release_exit(0); 126 | break; 127 | case 'h': 128 | default: 129 | printf("\'s\': search ipcam dev\n"); 130 | printf("\'r\': renew ipcam list\n"); 131 | printf("\'l\': list all ipcam dev\n"); 132 | printf("\'q\': quit\n"); 133 | printf("setipcname?ip=ip&name=name\n"); 134 | printf("\'h\': show this help\n"); 135 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 136 | fflush(stdout); 137 | break; 138 | } /* switch (cmd_string[0]) */ 139 | return ret; 140 | } 141 | 142 | static void search_ipcam(void) 143 | { 144 | int ret; 145 | struct ipcam_search_msg send_msg; 146 | 147 | memset(&send_msg, 0, sizeof(send_msg)); 148 | send_msg.type = IPCAMMSG_QUERY_ALIVE; 149 | send_msg.ssrc = SSRC; 150 | ret = broadcast_msg(IPCAM_SERVER_PORT, &send_msg, sizeof(send_msg)); 151 | if (ret < 0) 152 | debug_print("fail: sendto return %d", ret); 153 | 154 | return; 155 | } 156 | 157 | static char *get_line(char *s, size_t n, FILE *f) 158 | { 159 | char *p = fgets(s, n, f); 160 | 161 | if (p) { 162 | size_t last = strlen(s) - 1; 163 | 164 | if (s[last] == '\n') 165 | s[last] = '\0'; 166 | } 167 | return p; 168 | } 169 | 170 | static void release_exit(int signo) 171 | { 172 | /* 173 | * release resources 174 | */ 175 | free_ipcam_link(IPCAM_DEV); 176 | /* 177 | * exit 178 | */ 179 | exit(signo); 180 | } 181 | 182 | static void deal_msg_func(const struct ipcam_search_msg *msg, 183 | const struct sockaddr_in *from) 184 | { 185 | int ret; 186 | ipcam_info_t remote_ipcam_info; 187 | struct ipcam_node new_ipcam_node; 188 | pipcam_node tmp_node; 189 | 190 | memset(&remote_ipcam_info, 0, sizeof(remote_ipcam_info)); 191 | 192 | switch (msg->type) { 193 | case IPCAMMSG_LOGIN: 194 | debug_print("new ipcam login"); 195 | memcpy(&remote_ipcam_info.ipaddr, &from->sin_addr, 196 | (size_t)sizeof(struct in_addr)); 197 | memcpy(remote_ipcam_info.mac, msg->exten_msg, msg->exten_len); 198 | memcpy(remote_ipcam_info.ipcam_name, msg->ipcam_name, 199 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 200 | remote_ipcam_info.startup_time = msg->timestamp; 201 | debug_print("ipaddr is %s", inet_ntoa(remote_ipcam_info.ipaddr)); 202 | debug_print("mac is %02x:%02x:%02x:%02x:%02x:%02x", 203 | remote_ipcam_info.mac[0], 204 | remote_ipcam_info.mac[1], 205 | remote_ipcam_info.mac[2], 206 | remote_ipcam_info.mac[3], 207 | remote_ipcam_info.mac[4], 208 | remote_ipcam_info.mac[5]); 209 | debug_print("ipcam_name is %s\n", remote_ipcam_info.ipcam_name); 210 | debug_print("timestamp is %d\n", remote_ipcam_info.startup_time); 211 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 212 | new_ipcam_node.node_info = remote_ipcam_info; 213 | new_ipcam_node.alive_flag = 1; 214 | 215 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 216 | if (!ret) { 217 | tmp_node 218 | = search_ipcam_node_by_mac(IPCAM_DEV, 219 | remote_ipcam_info.mac); 220 | tmp_node->alive_flag = 1; 221 | } 222 | debug_print("insert return %d", ret); 223 | 224 | break; 225 | case IPCAMMSG_LOGOUT: 226 | debug_print("recv IPCAMMSG_LOGOUT msg"); 227 | ret = delete_ipcam_node_by_mac(IPCAM_DEV, msg->exten_msg); 228 | if (!ret) 229 | debug_print("it work badly"); 230 | 231 | break; 232 | case IPCAMMSG_HEARTBEAT: 233 | memcpy(&remote_ipcam_info.ipaddr, 234 | &from->sin_addr, 235 | (size_t)sizeof(struct in_addr)); 236 | memcpy(remote_ipcam_info.mac, 237 | msg->exten_msg, 238 | sizeof(remote_ipcam_info.mac)); 239 | memcpy(remote_ipcam_info.ipcam_name, 240 | msg->ipcam_name, 241 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 242 | memcpy(&remote_ipcam_info.startup_time, 243 | msg->exten_msg + sizeof(remote_ipcam_info.mac), 244 | sizeof(remote_ipcam_info.startup_time)); 245 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 246 | new_ipcam_node.node_info = remote_ipcam_info; 247 | new_ipcam_node.alive_flag = 1; 248 | 249 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 250 | if (!ret) { 251 | tmp_node = search_ipcam_node_by_mac(IPCAM_DEV, 252 | remote_ipcam_info.mac); 253 | tmp_node->alive_flag = 1; 254 | } 255 | 256 | break; 257 | case IPCAMMSG_ACK_ALIVE: 258 | memcpy(&remote_ipcam_info.ipaddr, &from->sin_addr, 259 | (size_t)sizeof(struct in_addr)); 260 | memcpy(remote_ipcam_info.mac, msg->exten_msg, 261 | sizeof(remote_ipcam_info.mac)); 262 | memcpy(remote_ipcam_info.ipcam_name, msg->ipcam_name, 263 | (size_t)sizeof(remote_ipcam_info.ipcam_name)); 264 | memcpy(&remote_ipcam_info.startup_time, 265 | msg->exten_msg + sizeof(remote_ipcam_info.mac), 266 | sizeof(remote_ipcam_info.startup_time)); 267 | debug_print("ipaddr is %s", inet_ntoa(remote_ipcam_info.ipaddr)); 268 | debug_print("mac is %02x:%02x:%02x:%02x:%02x:%02x", 269 | remote_ipcam_info.mac[0], 270 | remote_ipcam_info.mac[1], 271 | remote_ipcam_info.mac[2], 272 | remote_ipcam_info.mac[3], 273 | remote_ipcam_info.mac[4], 274 | remote_ipcam_info.mac[5]); 275 | debug_print("ipcam_name is %s\n", remote_ipcam_info.ipcam_name); 276 | debug_print("timestamp is %d\n", remote_ipcam_info.startup_time); 277 | memset(&new_ipcam_node, 0, sizeof(new_ipcam_node)); 278 | new_ipcam_node.node_info = remote_ipcam_info; 279 | new_ipcam_node.alive_flag = 1; 280 | 281 | ret = insert_nodulp_ipcam_node(IPCAM_DEV, &new_ipcam_node); 282 | if (!ret) { 283 | tmp_node 284 | = search_ipcam_node_by_mac(IPCAM_DEV, 285 | remote_ipcam_info.mac); 286 | tmp_node->alive_flag = 1; 287 | } 288 | break; 289 | case IPCAMMSG_ACK_DHCP: 290 | break; 291 | case IPCAMMSG_ACK_IP: 292 | break; 293 | default: 294 | debug_print("i am confused by the type of %d ", msg->type); 295 | } /* switch (msg->type) */ 296 | 297 | return; 298 | } /* static void deal_msg_func() */ 299 | 300 | static void clear_all_dev_online(ipcam_link IPCAM_DEV) 301 | { 302 | pipcam_node q = IPCAM_DEV->next; 303 | 304 | while (q) { 305 | q->alive_flag |= 1 << 1; 306 | q = q->next; 307 | } 308 | } 309 | 310 | static void test_all_dev_online(ipcam_link IPCAM_DEV) 311 | { 312 | pipcam_node q = IPCAM_DEV->next; 313 | 314 | while (q) { 315 | if (q->alive_flag != 1) 316 | q->alive_flag = 0; 317 | q = q->next; 318 | } 319 | } 320 | 321 | static int watch_ipcam_link_clear(struct aeEventLoop *loop, long long id, void *clientData) 322 | { 323 | clear_all_dev_online(IPCAM_DEV); 324 | aeCreateTimeEvent(loop, CHECK_IPCAM_CYCLE * 1000, watch_ipcam_link_test, NULL, NULL); 325 | return -1; 326 | } 327 | 328 | static int watch_ipcam_link_test(struct aeEventLoop *loop, long long id, void *clientData) 329 | { 330 | test_all_dev_online(IPCAM_DEV); 331 | clear_all_dev_online(IPCAM_DEV); 332 | return CHECK_IPCAM_CYCLE * 1000; 333 | } 334 | 335 | static int callback_list_ipcam(struct aeEventLoop *loop, long long id, void *clientData) 336 | { 337 | list_ipcam(IPCAM_DEV); 338 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 339 | fflush(stdout); 340 | return -1; 341 | } 342 | 343 | static void dealcmd(aeEventLoop *loop, int fd, void *privdata, int mask) 344 | { 345 | char line_buf[LINE_MAX] = {0}; 346 | char *curr_cmd = NULL; 347 | int ret; 348 | 349 | get_line(line_buf, sizeof(line_buf), stdin); 350 | curr_cmd = strtok(line_buf, ";"); 351 | if (curr_cmd) { 352 | ret = run_cmd_by_string(loop, curr_cmd); 353 | } else { 354 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 355 | fflush(stdout); 356 | } 357 | 358 | while ((curr_cmd = strtok(NULL, ";")) != NULL) { 359 | ret = run_cmd_by_string(loop, curr_cmd); 360 | debug_print("run_cmd_by_string return %d", ret); 361 | } 362 | } 363 | 364 | static int init_server_UDP_fd(int port, char *bindaddr) 365 | { 366 | int pc_server_fd; 367 | struct sockaddr_in pc_server; 368 | int ret; 369 | int sock_opt; 370 | 371 | pc_server_fd = socket(AF_INET, SOCK_DGRAM, 0); 372 | if (pc_server_fd == -1) { 373 | debug_print("socket fail"); 374 | exit(errno); 375 | } 376 | 377 | sock_opt = 1; 378 | ret = setsockopt(pc_server_fd, SOL_SOCKET, SO_REUSEADDR, 379 | &sock_opt, sizeof(sock_opt)); 380 | if (ret < 0) { 381 | debug_print("setsockopt fail"); 382 | exit(errno); 383 | } 384 | 385 | pc_server.sin_family = AF_INET; 386 | pc_server.sin_port = htons(port); 387 | pc_server.sin_addr.s_addr = htonl(INADDR_ANY); 388 | if (bindaddr && inet_aton(bindaddr, &pc_server.sin_addr) == 0) { 389 | debug_print("invalid bind address"); 390 | close(pc_server_fd); 391 | return -1; 392 | } 393 | 394 | ret = bind(pc_server_fd, (struct sockaddr*)&pc_server, 395 | sizeof(pc_server)); 396 | if (ret == -1) { 397 | debug_print("bind fail"); 398 | exit(errno); 399 | } 400 | return pc_server_fd; 401 | } 402 | 403 | static void dealnet(aeEventLoop *loop, int fd, void *privdata, int mask) 404 | { 405 | struct sockaddr_in peer; 406 | char buf[MAX_MSG_LEN]; 407 | struct ipcam_search_msg *msg_buf; 408 | socklen_t len; 409 | int ret; 410 | 411 | len = sizeof(struct sockaddr_in); 412 | ret = recvfrom(fd, buf, sizeof(buf), 0, 413 | (struct sockaddr *)&peer, (socklen_t *)&len); 414 | if (ret < 0) { 415 | debug_print("recvfrom fail"); 416 | return; 417 | } 418 | msg_buf = malloc(sizeof(struct ipcam_search_msg) 419 | + ((struct ipcam_search_msg *)buf)->exten_len); 420 | parse_msg(buf, ret, msg_buf); 421 | deal_msg_func(msg_buf, &peer); 422 | 423 | if (msg_buf) { 424 | free(msg_buf); 425 | msg_buf = NULL; 426 | } 427 | } 428 | 429 | int main(int argc, char **argv) 430 | { 431 | int ret; 432 | aeEventLoop *loop; 433 | 434 | if (already_running()) { 435 | fprintf(stderr, "already running!\n"); 436 | exit(1); 437 | } 438 | 439 | SSRC = getpid(); 440 | IPCAM_DEV = create_empty_ipcam_link(); 441 | 442 | int pc_server_fd; 443 | 444 | pc_server_fd = init_server_UDP_fd(PC_SERVER_PORT, "0.0.0.0"); 445 | assert(pc_server_fd > 0); 446 | loop = aeCreateEventLoop(); 447 | fprintf(stdout, "\033[01;32mipc_shell> \033[0m"); 448 | fflush(stdout); 449 | ret = aeCreateFileEvent(loop, STDIN_FILENO, AE_READABLE, dealcmd, NULL); 450 | assert(ret != AE_ERR); 451 | ret = aeCreateFileEvent(loop, pc_server_fd, AE_READABLE, dealnet, NULL); 452 | assert(ret != AE_ERR); 453 | aeCreateTimeEvent(loop, CHECK_IPCAM_CYCLE * 1000, watch_ipcam_link_clear, NULL, NULL); 454 | aeMain(loop); 455 | return 0; 456 | } 457 | -------------------------------------------------------------------------------- /ipcam_search_protocol.txt: -------------------------------------------------------------------------------- 1 | IP Cam设备搜寻协议(草案) 2 | 3 | 1. 通信方: 4 | 5 | * 网络摄像机(IP Cam), 6 | * pc机IP Cam设备搜寻工具. 7 | 8 | 2. 功能描述: 9 | 10 | 在pc机未知网络上IP Cam设备地址的情况下, 搜寻出所有提供服务的在线网络摄像 11 | 机. 对未配置好网络地址的IP Cam, 搜寻工具可对其进行网络设置. 对已配置好网 12 | 络地址的IP Cam, 设备搜寻工具可对其更改网络设置. 13 | 14 | IP Cam设备启动后, 向网络发送一次广播包, 内容为上线通知消息, pc机的IP Cam 15 | 设备搜寻工具接收到上线消息包后, 更新在线IP Cam设备队列. IP Cam设备关闭前, 16 | 向网络发送一次广播包, 内容为下线通知消息. pc机的IP Cam设备搜寻工具接收到 17 | 下线通知消息包后, 若该IP Cam设备在在线IP Cam设备队列里有记录, 将该IP Cam 18 | 设备从队列删除. 19 | 20 | IP Cam设备上线后, 以10s的周期向网络发送一次广播包, 内容为心跳消息, 该消息 21 | 记录了心跳次数. pc机的IP Cam设备搜寻工具监控在线IP Cam设备队列里面的每个 22 | IP Cam存活情况, 若30s内没有接收到一台IP Cam的心跳包, 将该IP Cam设备标记为 23 | 死亡, 直至重新接收到心跳包或上线通知包才将其激活. 24 | 25 | IP Cam设备搜寻工具可随时查询或设置网络上的IP Cam设备情况. 提供以下内容的 26 | 消息包: 27 | 28 | * 查询网络内所有的在线的IP Cam设备: 广播一次在线IP Cam查询消息, 每一台在 29 | 线的IP Cam设备收到此消息后, 往目的地址单播一个消息应答包, IP Cam设备搜寻 30 | 工具收到应答包 后跟新在线IP Cam队列信息. 31 | 32 | * 查询一台在线IP Cam设备状态: 单播一次IP Cam状态查询消息, 目标IP Cam收到 33 | 该消息后, 往申请查询的ip应答一个状态消息包. 34 | 35 | 3. 协议栈: 36 | 37 | +-----------+---------+-------------------------+ 38 | | IP首部 | UDP首部 | IPC SEARCH 消息结构 | 39 | +-----------+---------+-------------------------+ 40 | 20字节 8字节 见表2 41 | 42 | 表1. IPC SEARCH消息封装在UDP数据报文内 43 | 44 | 45 | 0 7 8 15 16 23 24 31 46 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 | | 消息类型 | 事务标识ID | Flags标记 | 扩展消息长度 | 48 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 49 | | 同步源标识符(SSRC) | 50 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51 | | 时间戳 | 52 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 53 | | 心跳数 | 54 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 55 | | | 56 | | IP Cam设备名称(64字节) | 57 | | | 58 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 59 | | | 60 | | 扩展消息(0~256字节) | 61 | | | 62 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 63 | 64 | 表2. IPC SEARCH 消息格式 (80~336字节) 65 | 66 | * 消息类型 67 | 68 | 上线通知消息: 0x1 69 | 下线通知消息: 0x2 70 | 心跳消息(dev 发向 pc 端): 0x3 71 | 在线查询消息: 0x4 72 | 在线查询回应: 0x5 73 | 要求目标机发起一次DHCP请求: 0x6 74 | 消息类型0x6的应答消息: 0x7 75 | 请求目标机按扩展消息内容设置ip: 0x8 76 | 消息类型0x8的应答消息: 0x9 77 | 78 | * 事务标识ID 79 | 80 | 发送一次消息后所引发目标机回应的一系列过程称为一次事务或会话. 例如: 81 | IP Cam搜寻工具广播一次在线查询消息包, 被网络上某一IP Cam设备接收后, 82 | 向发送 查询主机回应消息包, 是同一次事务. 83 | 同一次事务处理的事务标识ID相同. 两次不同的事务, 事务标识ID相同称为冲 84 | 突(碰撞). 应尽量避免冲突. 85 | 86 | * Flags标记 87 | 88 | 对于接收到消息后引发的操作, 若操作失败, 则反馈的消息将此标记设为1. 89 | 90 | * 扩展消息长度 91 | 92 | 指明了扩展消息的长度, 范围: 0~256. 93 | 94 | * 同步源标识符(SSRC) 95 | 96 | 同步源是产生该消息包的实体. SSRC是用于标明同步源的32位长度的随机数, 97 | 每次服务初始化时随机指定. 98 | 99 | * 时间戳 100 | 101 | 标识了该消息产生时的时间. 参考ICMP时间戳询问协议. 102 | 103 | * 心跳数 104 | 105 | IP Cam设备启动后, 以10s的周期广播心跳包, 第一个心跳包心跳数设为0, 下 106 | 一个心跳数递增, 直至溢出后从0开始. 107 | 108 | * IP Cam设备名称(64字节) 109 | 110 | 由IP Cam设备设置. 111 | 112 | * 扩展消息(0~256字节) 113 | 114 | 根据消息类型的不同有不同的值. 115 | 116 | 4. 端口号: 117 | 118 | IP Cam设备: 6755 119 | IP Cam搜寻工具: 6756 120 | 121 | -------------------------------------------------------------------------------- /para_parse.c: -------------------------------------------------------------------------------- 1 | #include "para_parse.h" 2 | #include 3 | 4 | char *para_parse_str(const char *src, const char *para, char *val) 5 | { 6 | char *p; 7 | char *q; 8 | 9 | q = strstr(src, para); 10 | if (!q) { 11 | *val = '\0'; 12 | return val; 13 | } 14 | if (*(q - 1) != '?' && *(q - 1) != '&') { 15 | *val = '\0'; 16 | return val; 17 | } 18 | if (*(q + strlen(para)) != '=') { 19 | *val = '\0'; 20 | return val; 21 | } 22 | q = strchr(q, '='); 23 | p = val; 24 | while (*++q) 25 | if (*q == '&') 26 | break; 27 | else 28 | *p++ = *q; 29 | *p = '\0'; 30 | return val; 31 | } 32 | 33 | int para_parse_int(const char *src, const char *key, int *val, int default_val) 34 | { 35 | char str[MAX_LINE] = {0}; 36 | 37 | para_parse_str(src, key, str); 38 | if (str[0] == '\0') { 39 | *val = default_val; 40 | return *val; 41 | } 42 | /* *val = atoi(str); */ 43 | *val = strtol(str, (char **)NULL, 10); 44 | return *val; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /para_parse.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARA_PARSE_H 2 | #define _PARA_PARSE_H 3 | 4 | #include 5 | 6 | #ifndef MAX_LINE 7 | #define MAX_LINE 1024 8 | #endif 9 | 10 | char *para_parse_str(const char *src, const char *key, char *def); 11 | int para_parse_int(const char *src, const char *key, int *val, int default_val); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /socket_wrap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "socket_wrap.h" 5 | #include "debug_print.h" 6 | 7 | int send_msg_by_sockaddr(const void *buf, size_t len, const struct sockaddr_in *from) 8 | { 9 | int ret; 10 | int socket_fd; 11 | 12 | socket_fd = socket(AF_INET, SOCK_DGRAM, 0); 13 | if (socket_fd == -1) { 14 | debug_print("socket fail"); 15 | exit(errno); 16 | } 17 | 18 | ret = sendto(socket_fd, buf, len, 19 | 0, (const struct sockaddr *)from, 20 | (socklen_t)sizeof(struct sockaddr_in)); 21 | 22 | if (ret < 0) { 23 | debug_print("sendto return %d", ret); 24 | } 25 | 26 | close(socket_fd); 27 | return ret; 28 | } 29 | 30 | int broadcast_msg(uint16_t dst_port, const void *buf, size_t len) 31 | { 32 | int ret; 33 | int socket_fd; 34 | struct sockaddr_in server; 35 | const int on = 1; 36 | 37 | socket_fd = socket(AF_INET, SOCK_DGRAM, 0); 38 | if (socket_fd == -1) { 39 | debug_print("socket fail"); 40 | exit(errno); 41 | } 42 | 43 | ret = setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); 44 | if (ret < 0) { 45 | debug_print("setsockopt fail"); 46 | exit(errno); 47 | } 48 | 49 | server.sin_family = AF_INET; 50 | server.sin_port = htons(dst_port); 51 | server.sin_addr.s_addr = inet_addr("255.255.255.255"); 52 | 53 | ret = sendto(socket_fd, buf, len, 54 | 0, (const struct sockaddr *)&server, 55 | (socklen_t)sizeof(server)); 56 | 57 | close(socket_fd); 58 | return ret; 59 | } 60 | -------------------------------------------------------------------------------- /socket_wrap.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCKET_WRAP_H 2 | #define _SOCKET_WRAP_H 3 | 4 | #include 5 | #include 6 | #if _LINUX_ 7 | #include 8 | #include 9 | #include 10 | #else 11 | #include 12 | typedef int socklen_t; 13 | #endif 14 | #include 15 | 16 | int send_msg_by_sockaddr(const void *buf, size_t len, const struct sockaddr_in *from); 17 | int broadcast_msg(uint16_t dst_port, const void *buf, size_t len); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /zmalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef _ZMALLOC_H 2 | #define _ZMALLOC_H 3 | 4 | #define zmalloc malloc 5 | 6 | #define zfree free 7 | 8 | #endif /* _ZMALLOC_H */ 9 | 10 | --------------------------------------------------------------------------------