]> git.decadent.org.uk Git - ap-utils.git/blobdiff - lib/snmp.c
Merge commit 'upstream/1.5'
[ap-utils.git] / lib / snmp.c
index 03bbf81a277a78bc6cef8a5c358b93b19e3654d5..cab421281a14ff1414c535daa2e841435b51c6ee 100644 (file)
@@ -3,7 +3,6 @@
  *     basic snmp packets assembly/disassembly and send/receive functions
  *
  * Copyright (c) Roman Festchook <roma at polesye dot net>
- *               Jan Rafaj <jr-aputils at cedric dot unob dot cz>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License Version 2 from
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <signal.h>
-#include <setjmp.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include "ap-utils.h"
 
-#define RETRIES 5
-
-sigjmp_buf position;
-
 extern char *community;
 extern short ap_type;
-extern int sockfd;
 extern struct in_addr ap_ip;
-int retries;
-char *buf = NULL;
 
-static void alarm_handler()
+int sockfd = 0, snmp_quit_by_keypress = 0;
+
+void close_sockfd()
+{
+    if (sockfd)
+       close(sockfd);
+}
+
+int open_sockfd()
+{
+    struct sockaddr_in client;
+
+    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+       return -1;
+
+    memset(&client, 0, sizeof client);
+    client.sin_family = AF_INET;
+    client.sin_port = INADDR_ANY;
+    client.sin_addr.s_addr = INADDR_ANY;
+
+    if (bind(sockfd, (struct sockaddr *) &client, SIZE) == -1)
+       return -1;
+
+    return 0;
+}
+
+int reopen_sockfd()
 {
-    retries--;
-    siglongjmp(position, 1);
+    close_sockfd();
+    return (open_sockfd());
 }
 
 /*
@@ -145,43 +162,75 @@ int ber(char *message, varbind * varbindlist, int num, int type)
 
 int snmp(varbind * varbindlist, int num, int type)
 {
-    unsigned char message[1024], *start;
+    static char buf[1024];
+    unsigned char *start;
     unsigned int num_reply;
-    int len;
+    int len = 0, tries = 5;
     struct sockaddr_in server;
+    struct timeval timeout;
+    fd_set rds;
 
     if (num == 0)
        return 1;
 
+    /*
+     * Flush sockfd by reopening. This prevents various 'something received/
+     * available on sockfd prior snmp() call' desync problems.
+     */
+    if (reopen_sockfd() == -1)
+       return 0;
+
     memset(&server, 0, sizeof server);
     server.sin_family = AF_INET;
     server.sin_port = htons(161);
     server.sin_addr.s_addr = ap_ip.s_addr;
 
-    signal(SIGALRM, alarm_handler);
-    retries = RETRIES;
-    sigsetjmp(position, 1);
-    if (!retries) {
-       return 0;
-    }
+    while (tries--) {
+       len = ber(buf, varbindlist, num, type);
+       if (sendto(sockfd, buf, len, 0, (struct sockaddr *) &server, SIZE)
+           == -1) {
+           return 0;
+       }
 
-    alarm(1);
-    len = ber(message, varbindlist, num, type);
-    if (sendto(sockfd, message, len, 0, (struct sockaddr *) &server, SIZE)
-       == -1) {
-       alarm(0);
-       return 0;
-    }
-    if ((len = recv(sockfd, message, 1024, 0)) == -1) {
-       alarm(0);
-       return 0;
+       FD_ZERO(&rds);
+       FD_SET(sockfd, &rds);
+       timeout.tv_sec = 1;
+       timeout.tv_usec = 0;
+
+       if (select(sockfd + 1, &rds, NULL, NULL, &timeout) == -1)
+           return 0;
+
+       if (FD_ISSET(sockfd, &rds)) {
+           if ((len = recv(sockfd, buf, sizeof buf, 0)) <= 0)
+               return 0;
+           else
+               break;
+       }
+
+       /* timeout => next try, as long as no key has been pressed */
+
+       /*
+        * Allow for quick 'last resort' escape using q/Q. Note:
+        * we may not use one select() for checking both fd 0 and sockfd, since
+        * something may appear on sockfd later than key has been pressed =>
+        * give gratuitous 1sec delay for response arrival to sockfd, and THEN
+        * just poll for anything on fd 0.
+        */
+       if (snmp_quit_by_keypress && type == GET) {
+           FD_ZERO(&rds);
+           FD_SET(0, &rds);
+           timeout.tv_sec = 0;
+           timeout.tv_usec = 0;
+           if (select(1, &rds, NULL, NULL, &timeout) == -1)
+               return 0;
+
+           if (FD_ISSET(0, &rds))
+               return 0;
+       }
     }
-    alarm(0);
 
-    if (buf)
-       free(buf);
-    buf = (char *) malloc(len);
-    memcpy(buf, message, len);
+    if (!tries)
+       return 0;
 
     start = buf;
     num_reply = 0;