source: trunk/instant_messenger/socket/BKP_20071105/server_bkp.c @ 151

Revision 151, 16.2 KB checked in by niltonneto, 16 years ago (diff)

Commit da nova versão do módulo, usando agente em C.
Vide Página do módulo do Trac:
http://www.expressolivre.org/dev/wiki/messenger

A versão anterior encontra-se na subpasta bkp (32/64).

Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/socket.h>
5#include <arpa/inet.h>
6#include <netdb.h>
7#include <unistd.h>
8#include <signal.h>
9#include <errno.h>
10#include <sys/wait.h>
11#include <sys/stat.h>
12#include <stdarg.h>
13#include <syslog.h>
14
15//#include <iostream>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <fcntl.h>
19#include <sys/time.h>
20#include <semaphore.h>
21#include <sys/mman.h>
22#include <stdarg.h>
23
24// Buffer size, in bytes, used for copying in the Cat() function.
25const size_t BUF_SIZE = 10240;
26
27// Typedef for a signal handler function.
28typedef void (*signal_handler_t) (int);
29
30// Prototypes for functions in this file.
31void ParseArgs (int, char **, struct in_addr *, unsigned short *, struct in_addr *, unsigned short *);
32void Initialise (void);
33void sig_child (int);
34int CreateServerSocket (struct in_addr, unsigned short);
35void Daemonise (void);
36void MainLoop (int, struct in_addr, unsigned short);
37void Proxy (int, int);
38int AcceptClientConnection (int);
39int ConnectToServer (struct in_addr, unsigned short);
40void Cat (int, int);
41int NameToAddr (const char *, struct in_addr *);
42int NameToPort (const char *, unsigned short *, const char *);
43#ifdef __GNUC__
44        void quit (const char *, ...) __attribute__ ((format (printf, 1, 2)));
45        void pbomb (const char *, ...) __attribute__ ((format (printf, 1, 2)));
46        void hbomb (const char *, ...) __attribute__ ((format (printf, 1, 2)));
47#else
48        void quit (const char *, ...);
49        void pbomb (const char *, ...);
50        void hbomb (const char *, ...);
51#endif
52void set_signal_handler (int, signal_handler_t);
53
54// Global variables
55char g_program_name[100]; // Initialised from argv[0] in ParseArgs()
56
57struct accept
58{
59        int newfd;
60        struct sockaddr_in sa;
61        struct in_addr jabber_addr;
62        unsigned short jabber_port;
63};
64
65int main (int argc, char *argv[])
66{
67        struct in_addr remote_addr,
68                                   local_addr;
69        unsigned short remote_port,
70                                   local_port;
71        int listen_fd;
72
73        ParseArgs(argc, argv, &remote_addr, &remote_port, &local_addr, &local_port);
74
75        Initialise();
76
77        // Create server socket before becoming
78        // a daemon so there is still a chance
79        // to print an error message.
80
81        listen_fd = CreateServerSocket(local_addr, local_port);
82        if ( listen_fd < 0 )
83                pbomb("Unable to create server socket");
84
85        Daemonise();
86
87        MainLoop(listen_fd, remote_addr, remote_port); // never returns
88
89        exit(EXIT_SUCCESS);
90}
91
92// ParseArgs()
93// Parse the command line arguments to extract the remote
94// and local adresses and port numbers, ra, rp, la & lp.
95// Exit the program gracefully upon error.
96void ParseArgs (int argc, char *argv[], struct in_addr *ra, unsigned short *rp, struct in_addr *la, unsigned short *lp)
97{
98         // argv[0] = program name
99         // argv[1] = remote_addr
100         // argv[2] = remote_port
101         // argv[3] = local_addr (optional)
102         // argv[4] = local_port (optional)
103
104        char * p = strrchr(argv[0], '/');
105
106        strncpy(g_program_name, (p == NULL) ? argv[0] : p + 1, sizeof(g_program_name) - 1);
107
108        if ( (argc < 3) || (argc > 5) )
109        {
110                fprintf (stderr, "usage: %s remote_addr remote_port [local_addr] [local_port]\n", argv[0]);
111                exit (EXIT_FAILURE);
112        }
113
114        if ( NameToAddr(argv[1], ra) )
115                hbomb("Unable to resolve \"%s\" to an ip address", argv[1]);
116
117        if ( NameToPort(argv[2], rp, "tcp") )
118                quit("Unable to resolve \"%s\" to a port number", argv[2]);
119
120        if ( argc < 4 )
121                la->s_addr = htonl(INADDR_ANY);
122        else
123                if ( NameToAddr(argv[3], la) )
124                        hbomb ("Unable to resolve \"%s\" to an ip address", argv[3]);
125
126        if ( argc < 5 )
127                memcpy(lp, rp, sizeof(*lp));
128        else
129                if ( NameToPort(argv[4], lp, "tcp") )
130                        quit("Unable to resolve \"%s\" to a port number", argv[4]);
131}
132
133// Initialise()
134// Setup syslog, signal handlers, and other intialisation.
135void Initialise (void)
136{
137        openlog(g_program_name, LOG_PID, LOG_USER);
138        syslog(LOG_INFO, "%s started", g_program_name);
139
140        chdir ("/"); // Change working directory to the root.
141
142        umask (0); // Clear our file mode creation mask
143
144        set_signal_handler(SIGCHLD, sig_child);
145
146        signal(SIGPIPE, SIG_IGN);
147}
148
149// sig_child()
150// Handles SIGCHLD from exiting child processes.
151void sig_child (int signo)
152{
153        pid_t pid = 1;
154
155        (void) signo; // suppress compiler warning
156
157        while ( pid > 0 )
158        {
159                pid = waitpid(WAIT_ANY, NULL, WNOHANG);
160
161                if ( pid > 0 )
162                        syslog(LOG_INFO, "Caught SIGCHLD from pid %d", pid);
163                //else
164                //      break;
165        }
166
167        if ( (pid < 0) && (errno != ECHILD) )
168                syslog(LOG_ERR, "waitpid(): %m"), exit(EXIT_FAILURE);
169
170        return;
171}
172
173// CreateServerSocket()
174// Create a socket, bind it to the specified address
175// and port, and set it to listen for client connections.
176// Returns < 0 on failure to bind, bombs on error otherwise,
177// returns the fd of the new socket on success.
178int CreateServerSocket (struct in_addr addr, unsigned short port)
179{
180        int err,
181                fd;
182        const int on = 1;
183        struct sockaddr_in sa;
184
185        // Create a socket and get its descriptor.
186        fd = socket(AF_INET, SOCK_STREAM, 0);
187        if ( fd < 0 )
188                syslog(LOG_ERR, "socket(): %m"), exit(EXIT_FAILURE);
189
190        // Set SO_REUSEADDR socket option
191        if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0 )
192                syslog(LOG_ERR, "setsockopt(fd%d, SO_REUSEADDR): %m", fd);
193
194        // Load a sa structure with the specified address and port
195        sa.sin_family = AF_INET;
196        sa.sin_port = htons(port);
197        sa.sin_addr = addr;
198        memset(sa.sin_zero, 0, sizeof(sa.sin_zero));
199
200        // Bind our socket to the address and port specified
201        err = bind(fd, (struct sockaddr *) &sa, sizeof(sa));
202        if ( err < 0 )
203        {
204                syslog(LOG_ERR, "bind(): %m");
205                return err;
206        }
207
208        // Tell socket to listen and queue up to 5 incoming connections.
209        if ( listen (fd, 5) < 0 )
210                syslog(LOG_ERR, "listen(): %m"), exit(EXIT_FAILURE);
211
212        return fd;
213}
214
215// Daemonise()
216// Put the program in the background, set
217// PPID=1, create a new session and process
218// group, without a controlling tty.
219void Daemonise (void)
220{
221        pid_t pid;
222
223        // Close stdin, stdout & stderr
224        // TODO:
225        // open /dev/null and dup the fd to stdin, stdout & stderr
226        // close(STDIN_FILENO);
227        // close(STDOUT_FILENO);
228        // close(STDERR_FILENO);
229
230        syslog (LOG_INFO, "%s daemonising", g_program_name);
231
232        // Fork the process to put it in the background.
233
234        pid = fork();
235        if ( pid == -1 )
236                syslog (LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
237
238        // parent terminates here, so shell thinks the command is done.
239        if ( pid )
240                exit(0);
241
242        syslog(LOG_INFO, "%s in background", g_program_name);
243
244        // 1st child continues to run in the background with PPID=1
245
246        // Become leader of a new session and a new process group,
247        // with no controlling tty.
248        setsid();
249
250        // Fork again to guarantee the process will not be able
251        // to aquire a controlling tty.
252
253        // signal (SIGHUP, SIG_IGN); // required according to Stevens' UNP2 p333
254
255        pid = fork();
256        if ( pid == -1 )
257                syslog(LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
258
259        if ( pid ) // 1st child terminates
260                exit(0);
261
262        // 2nd child continues, no longer a session or group leader
263
264        syslog(LOG_INFO, "%s daemonised", g_program_name);
265}
266
267struct shared {
268        sem_t mutex;
269        int count;
270        char * teste;//[10];
271} shared;
272
273void teste(void)
274{
275        puts("TESTE REFERENCIA DE FUNCAO\n\n");
276}
277
278void share_memory(void (* func)(void), ...)
279{
280        static int fd = (int)NULL;
281        int i,
282                nloop = 10;
283        static struct shared *ptr;
284        va_list ap;
285
286        if ( fd == (int)NULL )
287        {
288                if ( (fd = open("data.txt", O_RDWR | O_CREAT, 0660)) == -1 )
289                        fprintf(stderr,"error opening file\n");
290                write(fd, &shared, sizeof(struct shared));
291                ptr = (struct shared *) mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
292                close(fd);
293
294                if ( sem_init(&ptr->mutex, 1, 1) != 0 )
295                        fprintf(stderr,"sem_init error"), exit(-2);
296        }
297
298        setbuf(stdout, NULL);
299
300        sem_wait(&ptr->mutex);
301        printf("current value: %d\n", ptr->count++);
302        //va_start(func, ap);
303        //func(ap);
304        //va_end(ap);
305        sem_post(&ptr->mutex);
306}
307/*
308struct cli
309{
310        char user[100];
311};
312
313struct shared
314{
315        int count;
316        sem_t mutex;
317        struct cli clients[100];
318} shared;
319
320void teste(void)
321{
322        puts("TESTE REFERENCIA DE FUNCAO\n\n");
323}
324
325#define SHARED_MEMORY "/tmp/user_%04d.txt"
326
327void share_memory(void (* func)(void), ...)
328{
329        static struct shared * ptr = (struct shared *)NULL;
330        int fd = (int)NULL,
331                i = (int)NULL;
332        va_list ap;
333        char file_name[15];
334
335        setbuf(stdout, NULL);
336
337        bzero(file_name, 15);
338
339        if ( ptr == (struct shared *)NULL )
340        {
341                sprintf(file_name, SHARED_MEMORY, i++);
342
343                if ( (fd = open(file_name, O_RDWR | O_CREAT, 0660)) == -1 )
344                        fprintf(stderr,"error opening file for sharing memory\n");
345                write(fd, &shared, sizeof(struct shared));
346                ptr = (struct shared *) mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
347                close(fd);
348
349                if ( sem_init(&ptr->mutex, 1, 1) != 0 )
350                        fprintf(stderr,"sem_init error"), exit(-2);
351
352                ptr->count = (int)NULL;
353        }
354        sem_wait(&ptr->mutex);
355
356        //ptr->count++;
357        //bzero(ptr->clients[ptr->count].user, 100);
358        //sprintf(ptr->clients[ptr->count].user, "teste - %d", i);
359
360        //for ( i = 0; i <= ptr->count; i++)
361        //      printf("%d: %s\n", i, ptr->clients[i].user);
362
363
364        //va_start(func, ap);
365        //func(ap);
366        //va_end(ap);
367        sem_post(&ptr->mutex);
368}
369*/
370
371// MainLoop()
372// Classic concurrent server model.
373// Wait for a client to connect, fork a child process
374// to do the business with the client, parent process
375// continues to wait for the next connection.
376// This function does not return.
377void MainLoop (int listen_fd, struct in_addr rem_addr, unsigned short rem_port)
378{
379        int server_fd,
380                client_fd;
381        pid_t child_pid;
382
383        for ( ; ; )
384        {
385                client_fd = AcceptClientConnection (listen_fd);
386                share_memory(teste);
387
388                child_pid = fork();
389                if ( child_pid == -1 )
390                        syslog(LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
391
392                if ( child_pid ) // parent
393                {
394                        close(client_fd);
395
396                        syslog(LOG_INFO, "Forked child pid %d to deal with client", child_pid);
397                }
398                else // child
399                {
400                        close(listen_fd);
401
402                        server_fd = ConnectToServer(rem_addr, rem_port);
403
404                        Proxy(server_fd, client_fd);
405
406                        syslog(LOG_INFO, "exiting");
407                        _exit(0);
408                }
409        }
410}
411
412// Proxy()
413// Copies data between server_fd and client_fd
414// in both directions until one or other peer
415// closes their connection.
416void Proxy (int server_fd, int client_fd)
417{
418        pid_t helper_pid;
419
420        syslog(LOG_INFO, "Proxy(fd%d, fd%d)", server_fd, client_fd);
421
422        signal(SIGCHLD, SIG_IGN);
423
424        helper_pid = fork();
425        if ( helper_pid == -1 )
426                syslog(LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
427
428        if ( helper_pid ) // parent
429        {
430                syslog(LOG_INFO, "Forked child pid %d to help with proxying", helper_pid);
431
432                Cat(server_fd, client_fd);
433
434                shutdown(client_fd, 1);
435                close(server_fd);
436                close(client_fd);
437
438                wait(0); // wait for helper to exit
439        }
440        else // child (helper)
441        {
442                Cat(client_fd, server_fd);
443
444                shutdown(server_fd, 1);
445
446                syslog(LOG_INFO, "exiting");
447                exit(0); // helper exits here
448        }
449}
450
451// AcceptClientConnection()
452// waits for a tcp connect to the socket listen_fd, which
453// must already be bound and set to listen on a local port.
454// Bombs on error, returns the fd of the new socket on success.
455int AcceptClientConnection (int listen_fd)
456{
457        int newfd;
458        struct sockaddr_in sa;
459        socklen_t socklen;
460
461        syslog(LOG_INFO, "AcceptClientConnection(fd%d)", listen_fd);
462
463        // Accept the connection and create a new socket for it.
464        socklen = sizeof(sa);
465        memset(&sa, 0, socklen);
466        do
467        {
468                newfd = accept(listen_fd, (struct sockaddr *) &sa, &socklen);
469        }
470        while ( (newfd < 0) && (errno == EINTR) );
471
472        syslog(LOG_INFO, "Accepted client connection on new socket fd%d", newfd);
473
474        if ( newfd < 0 )
475                syslog(LOG_ERR, "accept(): %m"), exit(EXIT_FAILURE);
476
477        if ( socklen != sizeof(sa) )
478                syslog(LOG_ERR, "accept() screwed up!"), exit(EXIT_FAILURE);
479
480        return newfd;
481}
482
483// ConnectToServer()
484// attempts a tcp connect to the server specified
485// by addr and port.  Bombs on failure to connect,
486// returns the fd of the new socket on success.
487int ConnectToServer (struct in_addr addr, unsigned short port)
488{
489        // TODO:
490        // have a timeout for connect()
491        // see Unix socket FAQ 6.2
492
493        int fd,
494                err;
495        struct sockaddr_in sa;
496
497        // Create a socket and get its descriptor.
498        fd = socket(AF_INET, SOCK_STREAM, 0);
499        if ( fd < 0 )
500                syslog(LOG_ERR, "socket(): %m"), exit(EXIT_FAILURE);
501
502        sa.sin_family = AF_INET;
503        sa.sin_port = htons(port);
504        sa.sin_addr = addr;
505        memset(sa.sin_zero, 0, sizeof(sa.sin_zero));
506
507        err = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
508        if ( err < 0 )
509        {
510                syslog(LOG_ERR, "Unable to connect socket fd%d to server: %m", fd);
511                exit(EXIT_FAILURE);
512        }
513
514        syslog(LOG_INFO, "Connected socket fd%d to server", fd);
515
516        return fd;
517}
518
519// Cat()
520// read data from in_fd and write it to out_fd until
521// the connection is closed by one of the peers.
522// Data is copied using a dynamically allocated buffer.
523void Cat (int in_fd, int out_fd)
524{
525        unsigned char * const buf = malloc(BUF_SIZE);
526        int bytes_rcvd,
527                bytes_sent = 0,
528                i;
529
530        syslog(LOG_INFO, "Cat(fd%d, fd%d)", in_fd, out_fd);
531
532        if ( buf == NULL )
533                syslog(LOG_ERR, "malloc(): %m"), exit(EXIT_FAILURE);
534
535        do
536        {
537                bytes_rcvd = recv(in_fd, buf, BUF_SIZE, 0);
538
539                for ( i = 0; i < bytes_rcvd || bytes_sent < 0; i += bytes_sent )
540                {
541                        bytes_sent = send(out_fd, buf + i, bytes_rcvd - i, 0);
542
543                        //if ( bytes_sent < 0 )
544                        //      break;
545                }
546
547        }
548        while ( (bytes_rcvd > 0) && (bytes_sent > 0) );
549
550        if ( (bytes_rcvd < 0) && (errno != ECONNRESET) )
551                syslog(LOG_ERR, "recv(): %m"), exit(EXIT_FAILURE);
552
553        if ( (bytes_sent < 0) && (errno != EPIPE) )
554                syslog(LOG_ERR, "send(): %m"), exit(EXIT_FAILURE);
555
556        free(buf);
557}
558
559// NameToAddress()
560// Convert name to an ip address.
561// Returns 0 on success, -1 on failure.
562int NameToAddr (const char * name, struct in_addr * p_inaddr)
563{
564        struct hostent * he;
565
566        // First, attempt to convert from string ip format
567        // TODO:
568        // use inet_aton() instead
569        p_inaddr->s_addr = inet_addr(name);
570        if ( p_inaddr->s_addr != -1U )  // Success
571                return 0;
572
573        // Next, attempt to read from /etc/hosts or do a DNS lookup
574        he = gethostbyname(name);
575        if ( he != NULL ) // Success
576        {
577                memcpy(p_inaddr, he->h_addr, sizeof(struct in_addr));
578                return 0;
579        }
580
581        return -1; // Failed to resolve name to an ip address
582}
583
584// NameToPort()
585// Convert name to a port number. Name can either be a port name
586// (in which case proto must also be set to either "tcp" or "udp")
587// or name can be the ascii representation of the port number.
588// Returns 0 on success, -1 on failure.
589int NameToPort (const char * name, unsigned short * port, const char * proto)
590{
591        unsigned long lport;
592        char * errpos;
593        struct servent * se;
594
595        // First, attempt to convert string to integer
596        lport = strtoul(name, &errpos, 0);
597        if ( (*(errpos) == 0) && (lport <= 65535) ) // Success
598        {
599                *(port) = lport;
600                return 0;
601        }
602
603        // Next, attempt to read the string from /etc/services
604        se = getservbyname(name, proto);
605        if ( se != NULL ) // Success
606        {
607                *(port) = ntohs(se->s_port);
608                return 0;
609        }
610
611        return -1; // Failed to resolve port name to a number
612}
613
614// quit()
615// Print an error message to stderr
616// and syslog, then exit the program.
617void quit (const char *fmt, ...) // quit with msg
618{
619        va_list ap;
620
621        fflush(stdout);
622
623        fprintf(stderr, "%s: ", g_program_name);
624
625        va_start(ap, fmt);
626        vfprintf(stderr, fmt, ap);
627        va_end(ap);
628
629        fputc('\n', stderr);
630
631        syslog(LOG_ERR, "I quit!");
632
633        exit(EXIT_FAILURE);
634}
635
636// pbomb()
637// Print an error message to stderr
638// and syslog, then exit the program.
639// pbomb() additionally include the
640// string representation of errno.
641void pbomb (const char *fmt, ...) // bomb with perror
642{
643        va_list ap;
644        int errno_save = errno;
645        char buf[100];
646
647        fflush(stdout);
648
649        fprintf(stderr, "%s: ", g_program_name);
650
651        va_start(ap, fmt);
652        vsnprintf(buf, sizeof(buf), fmt, ap);
653        va_end(ap);
654
655        errno = errno_save;
656        perror(buf);
657
658        syslog(LOG_ERR, "Bang!: %s: %m", buf);
659
660        exit(EXIT_FAILURE);
661}
662
663// hbomb()
664// Print an error message to stderr
665// and syslog, then exit the program.
666// hbomb() additionally include the
667// string representation of h_errno.
668void hbomb (const char *fmt, ...) // bomb with herror
669{
670        va_list ap;
671        int h_errno_save = h_errno;
672        char buf[100];
673
674        fflush (stdout);
675
676        fprintf (stderr, "%s: ", g_program_name);
677
678        va_start(ap, fmt);
679        vsnprintf(buf, sizeof(buf), fmt, ap);
680        va_end(ap);
681
682        h_errno = h_errno_save;
683        herror(buf);
684
685        syslog(LOG_ERR, "Bang!: %s: %s", buf, hstrerror(h_errno));
686
687        exit(EXIT_FAILURE);
688}
689
690// set_signal_handler()
691// Sets a signal handler function.
692// Similar to signal() but this method
693// is more portable between platforms.
694void set_signal_handler (int signum, signal_handler_t sa_handler_func)
695{
696        struct sigaction act;
697
698        act.sa_handler = sa_handler_func;
699        sigemptyset (&(act.sa_mask));
700        act.sa_flags = 0;
701
702        if ( sigaction(signum, &act, NULL) < 0 )
703        {
704                syslog(LOG_ERR, "Error setting handler for signal %d: %m", signum);
705                exit(EXIT_FAILURE);
706        }
707}
Note: See TracBrowser for help on using the repository browser.