本文的运行环境是上一篇博客中搭建的apache2 cgi
cgi程序与普通的c语言程序几乎没啥区别,只是要注意以下几点:
1. 如果程序中需要执行root权限的命令,比如systemctl,需要将apache2的用户www-data设置成免输密码的sudoers,然后在程序中使用sudo systemctl 来运行
2. 获取apache传来的query string的方法是 getenv("QUERY_STRING")
3. cgi程序按照apache的要求必须先输出content-type
4. 标准输出将作为http request的返回结果
下面是一个修改debian11 ip地址的c语言cgi程序,因为用到了systemctl命令,所以要将www-data赋予root权限才能执行
/*
cd ~/code/ChangeIP/ && gcc main.c -o ChangeIP && sudo cp ChangeIP /var/www/cgi/changeip.cgi && cd /var/www/cgi && sudo chmod 777 changeip.cgi && ./changeip.cgi
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<net/if.h>
#include<netinet/in.h>
#include<string.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/ioctl.h>
#include<regex.h>
#define NameMaxLen 99
int get_all_networkcards(int first, char* first_card_name);
char* get_ip(char* ETH_NAME, char* ip);
int main(int argc, char** args)
{
const char* interfaces_path_edit = "/var/www/cgi/interfaces";
const char* interfaces_path = "/etc/network/interfaces";
char* new_ip = 0; int new_ip_len = 0; char new_ip_pre[16]; int new_ip_pre_ok = 0;
char first_card_name[NameMaxLen + 1]; char ip[100];
FILE* fd; char cp_cmd[1000];
printf("Content-type:text/html\r\n\r\n<br>\r\n-------change ip-----------<br>\r\n"); // required by cgi, a blank line must after the first line
if (argc > 1) { new_ip = args[1]; printf("args[1]=%s, argc=%d<br>\r\n", args[1], argc); }
else if (getenv("QUERY_STRING")) { new_ip = getenv("QUERY_STRING"); printf("query_string=%s<br>\r\n", new_ip); }
else goto invalid_new_ip;
if (new_ip == NULL) { goto invalid_new_ip; } printf("received new ip = %s<br>\r\n", new_ip);
new_ip_len = strlen(new_ip); if (new_ip_len < 7 || new_ip_len>15) { goto invalid_new_ip; }
strcpy(new_ip_pre, new_ip);
for (int i = new_ip_len - 1; i > 0; i--)
{
if (new_ip_pre[i] == '.') { new_ip_pre[i + 1] = 0; new_ip_pre_ok = 1; break; }
}
if (!new_ip_pre_ok) { goto invalid_new_ip; } printf("new ip pre: %s<br>\r\n", new_ip_pre);
if (get_all_networkcards(1, first_card_name) < 0) { printf("cannot find a netword card<br>\r\n"); return 0; }
printf("network card name: %s<br>\r\n", first_card_name);
if (get_ip(first_card_name, ip)) printf("current ip=%s<br>\r\n", ip);
remove(interfaces_path_edit);
fd = fopen(interfaces_path_edit, "w"); if (fd == NULL) { printf("cannot open file %s to write<br>\r\n", interfaces_path_edit); return 0; }
fprintf(fd, "auto "); fprintf(fd, first_card_name); fprintf(fd, "\n");
fprintf(fd, "iface "); fprintf(fd, first_card_name); fprintf(fd, " inet static\n");
fprintf(fd, "address "); fprintf(fd, new_ip); fprintf(fd, "\n");
fprintf(fd, "netmask 255.255.255.0\n");
fprintf(fd, "gateway "); fprintf(fd, new_ip_pre); fprintf(fd, "1\n");
fprintf(fd, "network "); fprintf(fd, new_ip_pre); fprintf(fd, "0\n");
fprintf(fd, "broadcase "); fprintf(fd, new_ip_pre); fprintf(fd, "255\n");
fflush(fd); fclose(fd);
strcpy(cp_cmd, "sudo cp "); strcat(cp_cmd, interfaces_path_edit); strcat(cp_cmd, " "); strcat(cp_cmd, interfaces_path); printf("%s<br>\r\n", cp_cmd);
system(cp_cmd); printf("updated config file\n");
usleep(10000);// sleep for 10 ms, wait for file copy complete
system("sudo systemctl restart networking &");
printf("update ip address succeed, new ip: %s<br>\r\n", new_ip); return 0;
invalid_new_ip: printf("invalid new ip<br>\r\n"); return 0;
}
int get_all_networkcards(int first, char* first_card_name)
{
struct ifreq ifr;
struct ifconf ifc;
char buf[2048];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) {
printf("socket error\n");
return -1;
}
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
printf("ioctl error\n");
return -1;
}
struct ifreq* it = ifc.ifc_req;
const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
char szMac[64];
int count = 0;
for (; it != end; ++it)
{
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0)
{
if (ifr.ifr_flags & IFF_LOOPBACK) continue; // don't count loopback
if (ioctl(sock, SIOCGIFHWADDR, &ifr)) continue;
count++;
if (first)
{
if (strlen(ifr.ifr_name) < NameMaxLen) { strcpy(first_card_name, ifr.ifr_name); return 1; }
else { printf("card name too long\n"); return -1; }
}
else
{
unsigned char* ptr;
ptr = (unsigned char*)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0];
snprintf(szMac, 64, "%02X:%02X:%02X:%02X:%02X:%02X", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5));
printf("%d. Interface name : %s , Mac address : %s \n", count, ifr.ifr_name, szMac);
}
}
else
{
printf("get mac info error\n");
return -1;
}
}
if (first) { printf("no card found\n"); return -1; }
return 1;
}
/*************************************************************
* 函数功能: 通过正则表达式检测是否为IP地址
* 参数类型: 需要检测的IP地址
* 返回类型: 成功返回0,失败返回-1
**************************************************************/
int check_right_ip(const char* ip)
{
int status = 0;
int cflags = REG_EXTENDED;
regmatch_t pmatch[1];
const size_t nmatch = 1;
regex_t reg;
char str_ip[30] = "";
const char* pattern = "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}";//存在局限
strcpy(str_ip, ip);
regcomp(®, pattern, cflags);
status = regexec(®, str_ip, nmatch, pmatch, 0);
if (status == REG_NOMATCH)
{
printf("No match\n");
return -1;
}
else if (status == 0)
{
return 0;
}
regfree(®);
return 0;
}
/*************************************************************
* 函数功能: 获取IP地址
* 参数类型: IP地址存放位置
* 返回类型:
**************************************************************/
char* get_ip(char* ETH_NAME, char* ip)
{
int sock;
struct sockaddr_in sin;
struct ifreq ifr;
char* temp_ip = NULL;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
{
perror("socket");
return NULL;
}
strncpy(ifr.ifr_name, ETH_NAME, IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
{
perror("ioctl");
return NULL;
}
memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
temp_ip = inet_ntoa(sin.sin_addr);
strcpy(ip, temp_ip);
fprintf(stdout, "eth0: %s\n", temp_ip);
return ip;
}
/*************************************************************
* 函数功能: 手动设置IP地址
* 参数类型: 要设置的IP地址
* 返回类型: 成功返回0,失败返回-1
**************************************************************/
int set_hand_ip(const char* ipaddr, char* ETH_NAME)
{
int sock_set_ip;
struct sockaddr_in sin_set_ip;
struct ifreq ifr_set_ip;
memset(&ifr_set_ip, 0, sizeof(ifr_set_ip));
if (ipaddr == NULL)
{
return -1;
}
sock_set_ip = socket(AF_INET, SOCK_STREAM, 0);
//printf("sock_set_ip=====%d\n",sock_set_ip);
if (sock_set_ip < 0)
{
perror("socket create failse...SetLocalIp!");
return -1;
}
memset(&sin_set_ip, 0, sizeof(sin_set_ip));
strncpy(ifr_set_ip.ifr_name, ETH_NAME, sizeof(ifr_set_ip.ifr_name) - 1);
sin_set_ip.sin_family = AF_INET;
sin_set_ip.sin_addr.s_addr = inet_addr(ipaddr);
memcpy(&ifr_set_ip.ifr_addr, &sin_set_ip, sizeof(sin_set_ip));
printf("ipaddr===%s\n", ipaddr);
if (ioctl(sock_set_ip, SIOCSIFADDR, &ifr_set_ip) < 0)
{
perror("Not setup interface");
return -1;
}
//设置激活标志
ifr_set_ip.ifr_flags |= IFF_UP | IFF_RUNNING;
//get the status of the device
if (ioctl(sock_set_ip, SIOCSIFFLAGS, &ifr_set_ip) < 0)
{
perror("SIOCSIFFLAGS");
return -1;
}
close(sock_set_ip);
return 0;
}