/************************************************************************* * (C) 2010 AITNET - Sofia/Bulgaria - * by Michael Pounov * * $Author: misho $ * $Id: upd.c,v 1.2 2011/06/08 12:45:41 misho Exp $ * *************************************************************************/ #include "global.h" #include "upd.h" int Kill; static inline int ChkImg(const char *csImg, char *psDir) { int res = 0; getcwd(psDir, MAXPATHLEN); if (access(csImg, R_OK) == -1) { printf("Error:: Unable to find new image %s #%d - %s\n", csImg, errno, strerror(errno)); res = -1; } else { strlcat(psDir, "/", MAXPATHLEN); strlcat(psDir, csImg, MAXPATHLEN); } return res; } // ------------------------------- int Activate(const char *csImg) { char szDir[MAXPATHLEN]; if (ChkImg(csImg, szDir) == -1) return -1; VERB(3) printf("Activate procedure for %s\n", szDir); unlink(FIRMWARE_IMG); if (symlink(szDir, FIRMWARE_IMG) == -1) { printf("Error:: Unable to activate new image %s #%d - %s\n", csImg, errno, strerror(errno)); return -2; } syslog(LOG_NOTICE, "Activate new image %s", csImg); VERB(1) printf("Activate new image %s\n", csImg); return 0; } int Install(const char *csImg, const char *psDir) { int src, dst, len; u_char buf[BUFSIZ]; char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; struct stat ss, ds; strlcpy(szFile, psDir, MAXPATHLEN); strlcat(szFile, "/", MAXPATHLEN); strlcat(szFile, csImg, MAXPATHLEN); if (access(szFile, R_OK) == -1) { printf("Error:: Unable to find new image %s #%d - %s\n", csImg, errno, strerror(errno)); return -1; } else { memset(&ss, 0, sizeof ss); if (stat(szFile, &ss) == -1) { printf("Error:: Unable to find new image %s #%d - %s\n", csImg, errno, strerror(errno)); return -1; } } getcwd(szDir, MAXPATHLEN); VERB(3) printf("Install procedure from %s to %s\n", szFile, szDir); strlcat(szDir, "/", MAXPATHLEN); strlcat(szDir, csImg, MAXPATHLEN); memset(&ds, 0, sizeof ds); if (stat(szDir, &ds) == -1 && ENOENT != errno) { printf("Error:: Unable to stat target #%d - %s\n", errno, strerror(errno)); return -1; } if (ss.st_dev == ds.st_dev && ss.st_ino == ds.st_ino) { printf("Error:: Unable to install into self ...\n"); return -1; } dst = open(szDir, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (dst == -1) { printf("Error:: in create image %s #%d - %s\n", szDir, errno, strerror(errno)); return -1; } src = open(szFile, O_RDONLY); if (src == -1) { printf("Error:: in open image %s #%d - %s\n", szFile, errno, strerror(errno)); close(dst); unlink(szDir); return -1; } while ((len = read(src, buf, BUFSIZ)) > 0) if (write(dst, buf, len) == -1) { printf("Error:: in write image #%d - %s\n", errno, strerror(errno)); close(src); close(dst); unlink(szDir); len = -1; break; } close(src); close(dst); if (!len) { syslog(LOG_NOTICE, "Install image %s to %s", csImg, szDir); VERB(1) printf("Install image %s to %s\n", csImg, szDir); } return len; } int Rollback(const char *csImg) { int src, dst, len; u_char buf[BUFSIZ]; char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; struct stat ss, ds; getcwd(szFile, MAXPATHLEN); strlcat(szFile, "/", MAXPATHLEN); strlcat(szFile, FIRMWARE_BAK, MAXPATHLEN); if (access(szFile, R_OK) == -1) { printf("Error:: Unable to find backup image #%d - %s\n", errno, strerror(errno)); return -1; } else { memset(&ss, 0, sizeof ss); if (stat(szFile, &ss) == -1) { printf("Error:: Unable to find backup image #%d - %s\n", errno, strerror(errno)); return -1; } } getcwd(szDir, MAXPATHLEN); strlcat(szDir, "/", MAXPATHLEN); strlcat(szDir, csImg, MAXPATHLEN); VERB(3) printf("Rollback procedure for image %s from backup!\n", csImg); memset(&ds, 0, sizeof ds); if (stat(szDir, &ds) == -1 && ENOENT != errno) { printf("Error:: Unable to stat target #%d - %s\n", errno, strerror(errno)); return -1; } if (ss.st_dev == ds.st_dev && ss.st_ino == ds.st_ino) { printf("Error:: Unable to rollback into self ...\n"); return -1; } src = open(szFile, O_RDONLY); if (src == -1) { printf("Error:: in open backup %s #%d - %s\n", szFile, errno, strerror(errno)); return -1; } dst = open(szDir, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (dst == -1) { printf("Error:: in create image %s #%d - %s\n", szDir, errno, strerror(errno)); close(src); return -1; } while ((len = read(src, buf, BUFSIZ)) > 0) if (write(dst, buf, len) == -1) { printf("Error:: in write image #%d - %s\n", errno, strerror(errno)); close(dst); close(src); unlink(szDir); len = -1; break; } close(dst); close(src); if (!len) { syslog(LOG_NOTICE, "Rollback image %s to %s", csImg, szDir); VERB(1) printf("Rollback image %s to %s\n", csImg, szDir); } return len; return 0; } int tFTP(const char *csImg, const char *psDir) { int src, dst, len; u_char buf[BUFSIZ]; char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; struct stat ss, ds; if (ChkImg(csImg, szDir) == -1) return -1; else { memset(&ss, 0, sizeof ss); if (stat(szDir, &ss) == -1) { printf("Error:: Unable to find image %s #%d - %s\n", szDir, errno, strerror(errno)); return -1; } } VERB(3) printf("tFTP procedure for %s to %s\n", szDir, psDir); strlcpy(szFile, psDir, MAXPATHLEN); strlcat(szFile, "/", MAXPATHLEN); strlcat(szFile, csImg, MAXPATHLEN); memset(&ds, 0, sizeof ds); if (stat(szFile, &ds) == -1 && ENOENT != errno) { printf("Error:: Unable to stat target %s #%d - %s\n", szFile, errno, strerror(errno)); return -1; } if (ss.st_dev == ds.st_dev && ss.st_ino == ds.st_ino) { printf("Error:: Unable to copy into self ...\n"); return -1; } dst = open(szFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (dst == -1) { printf("Error:: in create backup %s #%d - %s\n", szFile, errno, strerror(errno)); return -1; } src = open(szDir, O_RDONLY); if (src == -1) { printf("Error:: in open image %s #%d - %s\n", szDir, errno, strerror(errno)); close(dst); unlink(szFile); return -1; } while ((len = read(src, buf, BUFSIZ)) > 0) if (write(dst, buf, len) == -1) { printf("Error:: in write backup #%d - %s\n", errno, strerror(errno)); close(src); close(dst); unlink(szFile); len = -1; break; } close(src); close(dst); if (!len) { syslog(LOG_NOTICE, "Export tFTP image %s to %s", csImg, psDir); VERB(1) printf("Export tFTP image %s to %s\n", csImg, psDir); } return len; } int Backup(const char *csImg) { int src, dst, len; u_char buf[BUFSIZ]; char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; if (ChkImg(csImg, szDir) == -1) return -1; getcwd(szFile, MAXPATHLEN); strlcat(szFile, "/", MAXPATHLEN); strlcat(szFile, FIRMWARE_BAK, MAXPATHLEN); VERB(3) printf("Backup procedure for %s\n", szDir); dst = open(szFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (dst == -1) { printf("Error:: in create backup %s #%d - %s\n", szFile, errno, strerror(errno)); return -1; } src = open(szDir, O_RDONLY); if (src == -1) { printf("Error:: in open image %s #%d - %s\n", szDir, errno, strerror(errno)); close(dst); unlink(szFile); return -1; } while ((len = read(src, buf, BUFSIZ)) > 0) if (write(dst, buf, len) == -1) { printf("Error:: in write backup #%d - %s\n", errno, strerror(errno)); close(src); close(dst); unlink(szFile); len = -1; break; } close(src); close(dst); if (!len) { syslog(LOG_NOTICE, "Backup image %s", csImg); VERB(1) printf("Backup image %s\n", csImg); } return len; } int Clean(const char *csImg) { char szDir[MAXPATHLEN], szFile[MAXPATHLEN]; if (ChkImg(csImg, szDir) == -1) return -1; getcwd(szFile, MAXPATHLEN); strlcat(szFile, "/", MAXPATHLEN); strlcat(szFile, FIRMWARE_BAK, MAXPATHLEN); VERB(3) printf("Clean procedure for %s\n", szDir); if (unlink(szFile) == -1) { printf("Error:: in clean backup #%d - %s\n", errno, strerror(errno)); return -1; } syslog(LOG_NOTICE, "Clean backup for image %s", csImg); VERB(1) printf("Clean backup for image %s\n", csImg); return 0; } // ---------------------------------- static void Signal(int sig) { int stat; switch (sig) { case SIGHUP: VERB(5) printf("Info(5):: Signal arrived #%d\n", sig); break; case SIGCHLD: while (waitpid(-1, &stat, WNOHANG) > 0); break; case SIGTERM: Kill = 1; VERB(5) printf("Info(5):: Signal arrived #%d\n", sig); break; } } int Daemonize(struct sockaddr_in sin, const char *csTFTP) { int s, n = 1; pid_t pid; fd_set rfd; u_short seq = 0xffff; int clilen, len, f, retry; struct sockaddr_in cli, rcv; struct sigaction sa; u_char buf[TFTP_BUF], *pos; char szFName[MAX_STR]; struct tftphdr *tftp = (struct tftphdr*) buf; struct timeval tv = { TFTP_TIMEOUT, 0 }; VERB(3) printf("Daemonize procedure for %s:%d to %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), csTFTP); memset(&sa, 0, sizeof sa); sigemptyset(&sa.sa_mask); sa.sa_handler = Signal; sigaction(SIGCHLD, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) { printf("Error:: in create socket #%d - %s\n", errno, strerror(errno)); return -1; } if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n) == -1) { printf("Error:: in socket options #%d - %s\n", errno, strerror(errno)); close(s); return -1; } if (bind(s, (struct sockaddr*) &sin, sizeof sin) == -1) { printf("Error:: in bind #%d - %s\n", errno, strerror(errno)); close(s); return -1; } switch ((pid = fork())) { case -1: printf("Error:: in socket options #%d - %s\n", errno, strerror(errno)); close(s); return -1; case 0: setsid(); while (!Kill) { FD_ZERO(&rfd); FD_SET(s, &rfd); if (select(s + 1, &rfd, NULL, NULL, NULL) == -1) continue; memset(buf, 0, TFTP_BUF); clilen = sizeof cli; len = recvfrom(s, buf, TFTP_BUF, 0, (struct sockaddr*) &cli, (socklen_t*) &clilen); if (len == -1) { VERB(5) printf("Error:: in recvfrom #%d - %s\n", errno, strerror(errno)); continue; } tftp = (struct tftphdr*) (pos = buf); VERB(5) printf("Info(5):: Received packet from %s len %d with opcode=%hd\n", inet_ntoa(cli.sin_addr), len, ntohs(tftp->th_opcode)); switch (ntohs(tftp->th_opcode)) { case WRQ: len -= 2; pos += 2; strlcpy(szFName, (char*) pos, MAX_STR); VERB(5) printf("Info(5):: Get filename %s\n", szFName); len -= strlen((char*) pos) + 1; pos += strlen((char*) pos) + 1; VERB(5) printf("Info(5):: Get mode %s len=%d\n", pos, len); if (strncmp((char*) pos, "octet", len)) { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); tftp->th_code = htons(EBADOP); strncpy(tftp->th_data, "Error:: mode not supported", 27); sendto(s, buf, 31, 0, (struct sockaddr*) &cli, sizeof cli); continue; } VERB(2) printf("Info(2):: Receive file %s from %s\n", szFName, inet_ntoa(cli.sin_addr)); f = open(szFName, O_WRONLY | O_CREAT | O_EXCL, 0644); if (f == -1) { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); switch (errno) { case EACCES: case EPERM: tftp->th_code = htons(EACCESS); break; case EEXIST: tftp->th_code = htons(EEXISTS); break; default: tftp->th_code = htons(ENOSPACE); } snprintf(tftp->th_data, STRSIZ, "Error:: in file %s", strerror(errno)); sendto(s, buf, strlen(tftp->th_data) + 4, 0, (struct sockaddr*) &cli, sizeof cli); continue; } memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ACK); tftp->th_block = seq = 0; sendto(s, tftp, 4, 0, (struct sockaddr*) &cli, sizeof cli); for (retry = TFTP_RETRY; retry;) { FD_ZERO(&rfd); FD_SET(s, &rfd); if (select(s + 1, &rfd, NULL, NULL, &tv) < 1) { retry--; continue; } memset(buf, 0, TFTP_BUF); clilen = sizeof sin; if ((len = recvfrom(s, buf, TFTP_BUF, 0, (struct sockaddr*) &rcv, (socklen_t*) &clilen)) == -1) { VERB(5) printf("Error:: in recvfrom #%d - %s\n", errno, strerror(errno)); retry--; continue; } else if (cli.sin_addr.s_addr != rcv.sin_addr.s_addr) continue; VERB(5) printf("Info(5):: received opcode=%d block=%d seq=%d\n", ntohs(tftp->th_opcode), ntohs(tftp->th_block), seq); if (ERROR == ntohs(tftp->th_opcode)) { syslog(LOG_ERR, "Error:: in tftp receiving #%d - %s", tftp->th_code, tftp->th_data); close(f); unlink(szFName); break; } if (DATA == ntohs(tftp->th_opcode) && ++seq == ntohs(tftp->th_block)) { if (write(f, tftp->th_data, len - 4) == -1) { syslog(LOG_ERR, "Error:: Can`t write to file %s\n", szFName); close(f); unlink(szFName); break; } memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ACK); tftp->th_block = htons(seq); sendto(s, tftp, 4, 0, (struct sockaddr*) &rcv, sizeof rcv); VERB(5) printf("Info(5):: Send ACK for %s block=%d\n", inet_ntoa(cli.sin_addr), seq); if (SEGSIZE > len - 4) { close(f); syslog(LOG_INFO, "TFTP transfer complete to %s", szFName); break; } } else { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); tftp->th_code = htons(EBADID); snprintf(tftp->th_data, STRSIZ, "Error:: in transfer #%d", seq); sendto(s, buf, strlen(tftp->th_data) + 4, 0, (struct sockaddr*) &cli, sizeof cli); retry--; } } if (!retry) { close(f); unlink(szFName); syslog(LOG_ERR, "Error:: transfer aborted - timeout!"); } break; case RRQ: len -= 2; pos += 2; strlcpy(szFName, (char*) pos, MAX_STR); VERB(5) printf("Info(5):: Get filename %s\n", szFName); len -= strlen((char*) pos) + 1; pos += strlen((char*) pos) + 1; VERB(5) printf("Info(5):: Get mode %s len=%d\n", pos, len); if (strncmp((char*) pos, "octet", len)) { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); tftp->th_code = htons(EBADOP); strncpy(tftp->th_data, "Error:: mode not supported", 27); sendto(s, buf, 31, 0, (struct sockaddr*) &cli, sizeof cli); continue; } VERB(2) printf("Info(2):: Send file %s to %s\n", szFName, inet_ntoa(cli.sin_addr)); f = open(szFName, O_RDONLY); if (f == -1) { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); switch (errno) { case EACCES: case EPERM: tftp->th_code = htons(EACCESS); break; case ENOENT: tftp->th_code = htons(ENOTFOUND); break; default: tftp->th_code = htons(ENOSPACE); } snprintf(tftp->th_data, STRSIZ, "Error:: in file %s", strerror(errno)); sendto(s, buf, strlen(tftp->th_data) + 4, 0, (struct sockaddr*) &cli, sizeof cli); continue; } else seq = 1; for (retry = TFTP_RETRY; retry;) { tftp->th_opcode = htons(DATA); tftp->th_block = htons(seq); if ((len = read(f, tftp->th_data, SEGSIZE)) == -1) { syslog(LOG_ERR, "Error:: Can`t read from file %s\n", szFName); close(f); break; } else sendto(s, buf, len + 4, 0, (struct sockaddr*) &cli, sizeof cli); FD_ZERO(&rfd); FD_SET(s, &rfd); if (select(s + 1, &rfd, NULL, NULL, &tv) < 1) { retry--; continue; } memset(buf, 0, TFTP_BUF); clilen = sizeof sin; if (recvfrom(s, buf, TFTP_BUF, 0, (struct sockaddr*) &rcv, (socklen_t*) &clilen) == -1) { VERB(5) printf("Error:: in recvfrom #%d - %s\n", errno, strerror(errno)); retry--; continue; } else if (cli.sin_addr.s_addr != rcv.sin_addr.s_addr) continue; VERB(5) printf("Info(5):: received opcode=%d block=%d seq=%d\n", ntohs(tftp->th_opcode), ntohs(tftp->th_block), seq); if (ERROR == ntohs(tftp->th_opcode)) { syslog(LOG_ERR, "Error:: in tftp sending #%d - %s", tftp->th_code, tftp->th_data); if (lseek(f, len * -1, SEEK_CUR) == -1) { syslog(LOG_ERR, "Error:: revert sending #%d - %s", errno, strerror(errno)); close(f); break; } retry--; continue; } if (ACK == ntohs(tftp->th_opcode) && seq == ntohs(tftp->th_block)) { seq++; if (SEGSIZE > len) { close(f); syslog(LOG_INFO, "TFTP transfer complete to %s", szFName); break; } } else { memset(buf, 0, TFTP_BUF); tftp->th_opcode = htons(ERROR); tftp->th_code = htons(EBADID); snprintf(tftp->th_data, STRSIZ, "Error:: in transfer #%d", seq); sendto(s, buf, strlen(tftp->th_data) + 4, 0, (struct sockaddr*) &cli, sizeof cli); retry--; } } if (!retry) { close(f); syslog(LOG_ERR, "Error:: transfer aborted - timeout!"); } break; case DATA: if (seq == 0xffff) VERB(5) printf("Error:: not specified operation!\n"); break; } } break; } close(s); return 0; }