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

Revision 151, 17.1 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 cli
268{
269        char user[100];
270};
271
272struct client
273{
274        char user[100];
275        struct client * next;
276} client;
277
278struct shared {
279        sem_t mutex;
280        int count;
281        //struct cli clients[100];
282        struct client * clients;
283} shared;
284
285void teste(struct client * current)
286{
287        while ( current != (struct client *)NULL )
288        {
289                printf("%s...\n", current->user);
290                current = current->next;
291        }
292        //puts("\nFIM FUNCAO TEST\n\n");
293}
294
295void share_memory(void (* func)(struct client *))
296{
297        static struct shared * ptr = (struct shared *)NULL;
298        int fd = (int)NULL;
299        struct client * new,
300                                  * current;
301
302        if ( ptr == (struct shared *)NULL )
303        {
304                if ( (fd = open("/tmp/data.txt", O_RDWR | O_CREAT, 0660)) == -1 )
305                        fprintf(stderr,"error opening file\n");
306
307                write(fd, &shared, sizeof(struct shared));
308                ptr = (struct shared *) mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
309                close(fd);
310
311                fd = (int)NULL;
312
313                if ( sem_init(&ptr->mutex, 1, 1) != 0 )
314                        fprintf(stderr,"sem_init error"), exit(-2);
315
316                ptr->count = 0;
317                ptr->clients = (struct client *)NULL;
318        }
319
320        // begin mutex
321        sem_wait(&ptr->mutex);
322
323        if ( ptr->clients == (struct client *)NULL )
324        {
325                if ( (fd = open("/tmp/clients.txt", O_RDWR | O_CREAT, 0660)) == -1 )
326                        fprintf(stderr,"error opening file\n");
327                write(fd, &client, sizeof(struct client));
328                ptr->clients= (struct client *) mmap(NULL, 10 * sizeof(struct client), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
329                close(fd);
330
331                fd = (int)NULL;
332
333                current = ptr->clients;
334                current->next = (struct client *)NULL;
335        }
336        else
337        {
338                /*
339                current = ptr->clients;
340                if ( current->next == (struct client *)NULL )
341                {
342                        if ( (fd = open("clients.txt", O_RDWR | O_APPEND | O_CREAT, 0660)) == -1 )
343                                fprintf(stderr,"error opening file\n");
344
345                        puts("write");
346                        write(fd, &client, sizeof(struct client));
347                        puts("mmap");
348                        current->next = (struct client *) mmap(NULL, sizeof(struct client), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
349                        puts("close");
350                        close(fd);
351
352                        puts("ATRIBUINDO NULL");
353                        current->next->next = (struct client *)NULL;
354                        puts("CURRENT = CURRENT->NEXT");
355                        current = current->next;
356                        puts("__");
357                }
358                else
359                {
360                        puts("\nINICIADA\n");
361                }*/
362
363                current = ptr->clients;
364                while ( current->next != (struct client *)NULL )
365                        current = current->next;
366
367                /*if ( (fd = open("clients.txt", O_RDWR | O_APPEND | O_CREAT, 0660)) == -1 )
368                        fprintf(stderr,"error opening file\n");
369                write(fd, &client, sizeof(struct client));
370                new = / *(struct client *)* / mmap(NULL, sizeof(struct client), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
371                //current->next = (struct client *) mmap(NULL, sizeof(struct client), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
372                close(fd);*/
373
374                current = current->next = new;
375                current->next = (struct client *)NULL;
376        }
377
378        puts("\nWORKING\n\n");
379
380        setbuf(stdout, NULL);
381
382        printf("current value: %d\n", ptr->count);
383        bzero(current->user, 100);
384        sprintf(current->user, "teste - %d", ptr->count);
385
386        func(ptr->first);
387
388        ptr->count++;
389
390        // end mutex
391        sem_post(&ptr->mutex);
392}
393
394// MainLoop()
395// Classic concurrent server model.
396// Wait for a client to connect, fork a child process
397// to do the business with the client, parent process
398// continues to wait for the next connection.
399// This function does not return.
400void MainLoop (int listen_fd, struct in_addr rem_addr, unsigned short rem_port)
401{
402        int server_fd,
403                client_fd;
404        pid_t child_pid;
405
406        for ( ; ; )
407        {
408                client_fd = AcceptClientConnection (listen_fd);
409                share_memory(teste);
410
411                child_pid = fork();
412                if ( child_pid == -1 )
413                        syslog(LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
414
415                if ( child_pid ) // parent
416                {
417                        close(client_fd);
418
419                        syslog(LOG_INFO, "Forked child pid %d to deal with client", child_pid);
420                }
421                else // child
422                {
423                        close(listen_fd);
424
425                        server_fd = ConnectToServer(rem_addr, rem_port);
426
427                        Proxy(server_fd, client_fd);
428
429                        syslog(LOG_INFO, "exiting");
430                        _exit(0);
431                }
432        }
433}
434
435// Proxy()
436// Copies data between server_fd and client_fd
437// in both directions until one or other peer
438// closes their connection.
439void Proxy (int server_fd, int client_fd)
440{
441        pid_t helper_pid;
442
443        syslog(LOG_INFO, "Proxy(fd%d, fd%d)", server_fd, client_fd);
444
445        signal(SIGCHLD, SIG_IGN);
446
447        helper_pid = fork();
448        if ( helper_pid == -1 )
449                syslog(LOG_ERR, "fork(): %m"), exit(EXIT_FAILURE);
450
451        if ( helper_pid ) // parent
452        {
453                syslog(LOG_INFO, "Forked child pid %d to help with proxying", helper_pid);
454
455                Cat(server_fd, client_fd);
456
457                shutdown(client_fd, 1);
458                close(server_fd);
459                close(client_fd);
460
461                wait(0); // wait for helper to exit
462        }
463        else // child (helper)
464        {
465                Cat(client_fd, server_fd);
466
467                shutdown(server_fd, 1);
468
469                syslog(LOG_INFO, "exiting");
470                exit(0); // helper exits here
471        }
472}
473
474// AcceptClientConnection()
475// waits for a tcp connect to the socket listen_fd, which
476// must already be bound and set to listen on a local port.
477// Bombs on error, returns the fd of the new socket on success.
478int AcceptClientConnection (int listen_fd)
479{
480        int newfd;
481        struct sockaddr_in sa;
482        socklen_t socklen;
483
484        syslog(LOG_INFO, "AcceptClientConnection(fd%d)", listen_fd);
485
486        // Accept the connection and create a new socket for it.
487        socklen = sizeof(sa);
488        memset(&sa, 0, socklen);
489        do
490        {
491                newfd = accept(listen_fd, (struct sockaddr *) &sa, &socklen);
492        }
493        while ( (newfd < 0) && (errno == EINTR) );
494
495        syslog(LOG_INFO, "Accepted client connection on new socket fd%d", newfd);
496
497        if ( newfd < 0 )
498                syslog(LOG_ERR, "accept(): %m"), exit(EXIT_FAILURE);
499
500        if ( socklen != sizeof(sa) )
501                syslog(LOG_ERR, "accept() screwed up!"), exit(EXIT_FAILURE);
502
503        return newfd;
504}
505
506// ConnectToServer()
507// attempts a tcp connect to the server specified
508// by addr and port.  Bombs on failure to connect,
509// returns the fd of the new socket on success.
510int ConnectToServer (struct in_addr addr, unsigned short port)
511{
512        // TODO:
513        // have a timeout for connect()
514        // see Unix socket FAQ 6.2
515
516        int fd,
517                err;
518        struct sockaddr_in sa;
519
520        // Create a socket and get its descriptor.
521        fd = socket(AF_INET, SOCK_STREAM, 0);
522        if ( fd < 0 )
523                syslog(LOG_ERR, "socket(): %m"), exit(EXIT_FAILURE);
524
525        sa.sin_family = AF_INET;
526        sa.sin_port = htons(port);
527        sa.sin_addr = addr;
528        memset(sa.sin_zero, 0, sizeof(sa.sin_zero));
529
530        err = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
531        if ( err < 0 )
532        {
533                syslog(LOG_ERR, "Unable to connect socket fd%d to server: %m", fd);
534                exit(EXIT_FAILURE);
535        }
536
537        syslog(LOG_INFO, "Connected socket fd%d to server", fd);
538
539        return fd;
540}
541
542// Cat()
543// read data from in_fd and write it to out_fd until
544// the connection is closed by one of the peers.
545// Data is copied using a dynamically allocated buffer.
546void Cat (int in_fd, int out_fd)
547{
548        unsigned char * const buf = malloc(BUF_SIZE);
549        int bytes_rcvd,
550                bytes_sent = 0,
551                i;
552
553        syslog(LOG_INFO, "Cat(fd%d, fd%d)", in_fd, out_fd);
554
555        if ( buf == NULL )
556                syslog(LOG_ERR, "malloc(): %m"), exit(EXIT_FAILURE);
557
558        do
559        {
560                bytes_rcvd = recv(in_fd, buf, BUF_SIZE, 0);
561
562                for ( i = 0; i < bytes_rcvd || bytes_sent < 0; i += bytes_sent )
563                {
564                        bytes_sent = send(out_fd, buf + i, bytes_rcvd - i, 0);
565
566                        //if ( bytes_sent < 0 )
567                        //      break;
568                }
569
570        }
571        while ( (bytes_rcvd > 0) && (bytes_sent > 0) );
572
573        if ( (bytes_rcvd < 0) && (errno != ECONNRESET) )
574                syslog(LOG_ERR, "recv(): %m"), exit(EXIT_FAILURE);
575
576        if ( (bytes_sent < 0) && (errno != EPIPE) )
577                syslog(LOG_ERR, "send(): %m"), exit(EXIT_FAILURE);
578
579        free(buf);
580}
581
582// NameToAddress()
583// Convert name to an ip address.
584// Returns 0 on success, -1 on failure.
585int NameToAddr (const char * name, struct in_addr * p_inaddr)
586{
587        struct hostent * he;
588
589        // First, attempt to convert from string ip format
590        // TODO:
591        // use inet_aton() instead
592        p_inaddr->s_addr = inet_addr(name);
593        if ( p_inaddr->s_addr != -1U )  // Success
594                return 0;
595
596        // Next, attempt to read from /etc/hosts or do a DNS lookup
597        he = gethostbyname(name);
598        if ( he != NULL ) // Success
599        {
600                memcpy(p_inaddr, he->h_addr, sizeof(struct in_addr));
601                return 0;
602        }
603
604        return -1; // Failed to resolve name to an ip address
605}
606
607// NameToPort()
608// Convert name to a port number. Name can either be a port name
609// (in which case proto must also be set to either "tcp" or "udp")
610// or name can be the ascii representation of the port number.
611// Returns 0 on success, -1 on failure.
612int NameToPort (const char * name, unsigned short * port, const char * proto)
613{
614        unsigned long lport;
615        char * errpos;
616        struct servent * se;
617
618        // First, attempt to convert string to integer
619        lport = strtoul(name, &errpos, 0);
620        if ( (*(errpos) == 0) && (lport <= 65535) ) // Success
621        {
622                *(port) = lport;
623                return 0;
624        }
625
626        // Next, attempt to read the string from /etc/services
627        se = getservbyname(name, proto);
628        if ( se != NULL ) // Success
629        {
630                *(port) = ntohs(se->s_port);
631                return 0;
632        }
633
634        return -1; // Failed to resolve port name to a number
635}
636
637// quit()
638// Print an error message to stderr
639// and syslog, then exit the program.
640void quit (const char *fmt, ...) // quit with msg
641{
642        va_list ap;
643
644        fflush(stdout);
645
646        fprintf(stderr, "%s: ", g_program_name);
647
648        va_start(ap, fmt);
649        vfprintf(stderr, fmt, ap);
650        va_end(ap);
651
652        fputc('\n', stderr);
653
654        syslog(LOG_ERR, "I quit!");
655
656        exit(EXIT_FAILURE);
657}
658
659// pbomb()
660// Print an error message to stderr
661// and syslog, then exit the program.
662// pbomb() additionally include the
663// string representation of errno.
664void pbomb (const char *fmt, ...) // bomb with perror
665{
666        va_list ap;
667        int errno_save = errno;
668        char buf[100];
669
670        fflush(stdout);
671
672        fprintf(stderr, "%s: ", g_program_name);
673
674        va_start(ap, fmt);
675        vsnprintf(buf, sizeof(buf), fmt, ap);
676        va_end(ap);
677
678        errno = errno_save;
679        perror(buf);
680
681        syslog(LOG_ERR, "Bang!: %s: %m", buf);
682
683        exit(EXIT_FAILURE);
684}
685
686// hbomb()
687// Print an error message to stderr
688// and syslog, then exit the program.
689// hbomb() additionally include the
690// string representation of h_errno.
691void hbomb (const char *fmt, ...) // bomb with herror
692{
693        va_list ap;
694        int h_errno_save = h_errno;
695        char buf[100];
696
697        fflush (stdout);
698
699        fprintf (stderr, "%s: ", g_program_name);
700
701        va_start(ap, fmt);
702        vsnprintf(buf, sizeof(buf), fmt, ap);
703        va_end(ap);
704
705        h_errno = h_errno_save;
706        herror(buf);
707
708        syslog(LOG_ERR, "Bang!: %s: %s", buf, hstrerror(h_errno));
709
710        exit(EXIT_FAILURE);
711}
712
713// set_signal_handler()
714// Sets a signal handler function.
715// Similar to signal() but this method
716// is more portable between platforms.
717void set_signal_handler (int signum, signal_handler_t sa_handler_func)
718{
719        struct sigaction act;
720
721        act.sa_handler = sa_handler_func;
722        sigemptyset (&(act.sa_mask));
723        act.sa_flags = 0;
724
725        if ( sigaction(signum, &act, NULL) < 0 )
726        {
727                syslog(LOG_ERR, "Error setting handler for signal %d: %m", signum);
728                exit(EXIT_FAILURE);
729        }
730}
Note: See TracBrowser for help on using the repository browser.