一、系统/平台
系统:openEuler-22.03
硬件平台:aarch64
二、问题
有个系统升级的需求,java端负责OTA升级包的下载,和版本维护,C端完成系统升级的后续操作,这时候就需要java端在下载完OTA升级包并校验通过之后,通知C端去完成系统升级。其中有三种方式可以完成这种进程间通讯:
1. 本地sock(UNIX Domain Socket)(最终选择的方式)
2. 读文件的方式:inotify 机制来读取
3. UDP网络通讯
备注:问题就是,
1. 通过本地socket的方式在/tmp目录下创建的domain socket文件,或通过读文件的方式在/tmp目录下创建一个.txt文件,在系统命令下调用都正常,但是通过systemd的service来调用就失败,最后的解决方案是,domain文件或.txt文件放到了/run目录下。(具体原因未找到)。
三、最终代码
ota.c (部分代码)
#include <stdio.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <stdbool.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
#define UNIX_DOMAIN_FILE "/run/OTA_SOCKET.domain"
int listen_socket_create() {
int fd = 0;
int b_reuse = 1;
struct sockaddr_un sun;
if((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
printf("socket\n");
exit(-1);
}
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
bzero(&sun, sizeof(struct sockaddr_un));
sun.sun_family = AF_LOCAL;
if(!access(UNIX_DOMAIN_FILE, F_OK)) {
unlink(UNIX_DOMAIN_FILE);//删除该文件
}
strncpy(sun.sun_path, UNIX_DOMAIN_FILE, strlen(UNIX_DOMAIN_FILE));
if(bind(fd, (struct sockaddr *)&sun, sizeof(sun))) {
printf("bind\n");
}
if(listen(fd, 2) < 0) {
printf("listen\n");
}
return fd;
}
void *ota_listen_pro(void *argv) {
int newfd = -1;
int read_len = 0;
struct sockaddr_un cun;
char buffer[15] = {0};
const char *ota_msg = "ota_notice";
socklen_t cun_addr_len = sizeof(cun);
int listen_fd = listen_socket_create();
while(1) {
if((newfd = accept(listen_fd, (struct sockaddr *)&cun, &cun_addr_len)) < 0) {
printf("accept error\n");
}
read_len = read(newfd, buffer, sizeof(buffer));
if(!strncmp(ota_msg, buffer, strlen(ota_msg))) {
printf("Enter OTA update\n");
//enter_ota();
}
}
return NULL;
}
void init_ota_listen() {
pthread_t ota_listen_tid;
pthread_create(&ota_listen_tid, NULL, ota_listen_pro, NULL);
pthread_detach(ota_listen_tid);
return;
}
int main()
{
init_ota_listen();
}
ota_notice.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "./rst_key.h"
#include <sys/un.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <strings.h>
#define UNIX_DOMAIN_FILE "/run/OTA_SOCKET.domain"
int main(int argc, const char *argv[])
{
int timeout = 5;
int fd;
struct sockaddr_un sun;
const char buffer[] = "ota_notice";
if((fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(-1);
}
bzero(&sun, sizeof(sun));
sun.sun_family = AF_LOCAL;
while((access(UNIX_DOMAIN_FILE, F_OK|W_OK) < 0)) {
sleep(1);
if(!timeout--) {
exit(-1);
}
}
strncpy(sun.sun_path, UNIX_DOMAIN_FILE, strlen(UNIX_DOMAIN_FILE));
if(connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
perror("connect");
}
write(fd, buffer, sizeof(buffer));
return 0;
}
/etc/systemd/system/ota_notice.service
[Unit]
Description=OTA notice Service
After=rc-local.service
[Service]
Type=simple
#ExecStart=/root/test.sh
WorkingDirectory=/root
ExecStart=/usr/soft/jdk1.8.0_201/bin/java JavaExecuteShell
PrivateTmp=true
[Install]
WantedBy=multi-user.target
/root/test.sh
#!/bin/bash
echo "execute......" > /root/test.log
/root/ota_notice
echo "done" >> /root/test.log
JavaExecuteShell.java
/**
* java程序调用shell脚本
*/
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
import java.io.IOException;
public class JavaExecuteShell {
public static void main(String[] args) {
try {
//String cmd = "sh /root/test.sh "+args[0] +" "+args[1];
String cmd = "sh /root/test.sh ";
System.out.println("cmd = "+cmd);
Process exec = Runtime.getRuntime().exec(cmd);
String line;
//要执行的程序可能输出较多,而运行窗口的缓冲区有限,会造成waitFor阻塞,利用这种方式可以在waitFor命令之前读掉输出缓冲区的内容
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()));
while ((line = bufferedReader.readLine())!=null){
System.out.println("result======="+line);
}
bufferedReader.close();
//等待脚本执行完成
exec.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
四、部署/运行
部署:
cp ota ota_notice JavaExecuteShell.class test.sh /root
cp ota_notice.service /etc/systemd/system/ota_notice.service
运行:
/root/ota &
sleep1
systemctl start ota_notice
sleep 1
systemctl status ota_notice