388 lines
12 KiB
C++
388 lines
12 KiB
C++
/*************************************************************************
|
||
> File Name: migrate.cpp
|
||
> Author: TianLun Song
|
||
> Mail: songtianlun@frytea.com
|
||
> Blog: https://blog.frytea.com
|
||
> Created Time: Wed 23 Dec 2020 07:10:09 PM CST
|
||
************************************************************************/
|
||
|
||
#include<iostream>
|
||
#include<algorithm> /* sort() */
|
||
#include<sys/types.h>
|
||
#include<sys/stat.h>
|
||
#include<fcntl.h>
|
||
#include<unistd.h>
|
||
#include<string.h>
|
||
#include<stdio.h>
|
||
#include<stdlib.h>
|
||
#include<sys/types.h>
|
||
#include<sys/socket.h>
|
||
|
||
class CMigrateApp
|
||
{
|
||
#define CRIU_BINARY "/usr/sbin/criu"
|
||
#define CRIU_DIR "/tmp/criu"
|
||
#define IMG_DIR "images"
|
||
#define DUMP_LOG_FILE "dump.log"
|
||
#define RESTORE_LOG_FILE "restore.log"
|
||
#define RESTORE_PID_FILE "restore.pid"
|
||
#define INHERIT_FD_OPTION "--inherit-fd"
|
||
#define OLD_LOG_FILE "/tmp/criu/oldlog"
|
||
#define NEW_LOG_FILE "/tmp/criu/newlog"
|
||
|
||
public:
|
||
// CMigrateApp(int iPid = 0);
|
||
CMigrateApp(char *sCMD, int iPid = 0);
|
||
~CMigrateApp(){}
|
||
char *GetCMD();
|
||
char *GetImgDir();
|
||
int Checkout();
|
||
int Restore();
|
||
int GetPid();
|
||
int GetUnixNum();
|
||
|
||
private:
|
||
int GetPidByCMD(char * sCMD);
|
||
int SetPidByCMD(char * sCMD);
|
||
void GetCMDByPid(int iPid, char *sCMD);
|
||
int isUnixActivity(unsigned int iNode, char * cPid);
|
||
int SetUnixByPid(int iPid);
|
||
void Mkdir_safe(char *dirname, int mode);
|
||
|
||
private:
|
||
int i_criu_pid;
|
||
int i_unix_num;
|
||
char s_criu_cmd[64];
|
||
char s_criu_pid[8];
|
||
char s_criu_img[64];
|
||
char s_criu_dump_log[64];
|
||
char s_criu_restore_log[64];
|
||
char s_criu_restore_pidfile[64];
|
||
char s_external_sk_ino[1024];
|
||
char s_inh_unixsk_arg[1024];
|
||
const char *s_dump_argv[15] = {
|
||
"criu", "dump", "-j",
|
||
"-D", s_criu_img, "-o", s_criu_dump_log,
|
||
"-v4", "-x", "--lib /root/stl/lib",
|
||
"--tcp-established",
|
||
s_external_sk_ino,
|
||
"-t", s_criu_pid,
|
||
NULL };
|
||
const char *s_restore_argv[18] = {
|
||
"criu", "restore", "-d","-j",
|
||
"-D", s_criu_img, "-o", s_criu_restore_log,
|
||
"--pidfile", s_criu_restore_pidfile,
|
||
"-v4", "-x", "--lib /root/stl/lib",
|
||
"--tcp-established",
|
||
s_inh_unixsk_arg,
|
||
NULL };
|
||
};
|
||
|
||
CMigrateApp::CMigrateApp(char* sCMD, int iPid)
|
||
{
|
||
char sMkdir[64] = {0};
|
||
|
||
// init data
|
||
memset(s_criu_img, 0x00, sizeof s_criu_img);
|
||
memset(s_criu_dump_log, 0x00, sizeof s_criu_dump_log);
|
||
memset(s_criu_restore_log, 0x00, sizeof s_criu_restore_log);
|
||
memset(s_criu_restore_pidfile, 0x00, sizeof s_criu_restore_pidfile);
|
||
memset(s_criu_cmd, 0x00, sizeof s_criu_cmd);
|
||
i_criu_pid = 0;
|
||
i_unix_num = 0;
|
||
memset(s_criu_pid, 0x00, sizeof s_criu_pid);
|
||
memset(s_external_sk_ino, 0x00, sizeof s_external_sk_ino);
|
||
memset(s_inh_unixsk_arg, 0x00, sizeof s_inh_unixsk_arg);
|
||
|
||
if( 0 != iPid )
|
||
{
|
||
GetCMDByPid(iPid, s_criu_cmd);
|
||
i_criu_pid = iPid;
|
||
snprintf(s_criu_pid, sizeof s_criu_pid, "%d", i_criu_pid);
|
||
}
|
||
else if( NULL != sCMD )
|
||
{
|
||
strcpy(s_criu_cmd, sCMD);
|
||
SetPidByCMD(s_criu_cmd);
|
||
}
|
||
else
|
||
{
|
||
printf("Error of [iPid: %d] or [sCMD: %s] ", iPid, sCMD);
|
||
}
|
||
|
||
snprintf(s_criu_img, sizeof s_criu_img, "%s/%s/%s", CRIU_DIR, s_criu_cmd, IMG_DIR);
|
||
snprintf(s_criu_dump_log, sizeof s_criu_dump_log, "%s/%s/%s", CRIU_DIR, s_criu_cmd, DUMP_LOG_FILE);
|
||
snprintf(s_criu_restore_log, sizeof s_criu_restore_log, "%s/%s/%s", CRIU_DIR, s_criu_cmd, RESTORE_LOG_FILE);
|
||
snprintf(s_criu_restore_pidfile, sizeof s_criu_restore_pidfile, "%s/%s/%s", CRIU_DIR, s_criu_cmd, RESTORE_PID_FILE);
|
||
i_unix_num = SetUnixByPid(i_criu_pid);
|
||
// printf("PCMD: %s, Unix Sum: %d\n",sCMD, i_unix_num);
|
||
|
||
sprintf(sMkdir,"mkdir -p %s &", s_criu_img);
|
||
system(sMkdir);
|
||
printf("App will be to Migrate: [iPid: %d] [CMD: %s] [Unix Num: %d]\n", i_criu_pid, s_criu_cmd, i_unix_num);
|
||
}
|
||
|
||
int CMigrateApp::Checkout()
|
||
{
|
||
char sCmd[2048] = {0};
|
||
char sCpCMd[64] = {0};
|
||
if(0 >= i_criu_pid)
|
||
{
|
||
printf("Error Pid! Dump failed!\n");
|
||
return -1;
|
||
}
|
||
|
||
for (int i = 0; s_dump_argv[i] != NULL; i++)
|
||
sprintf(sCmd, "%s %s", sCmd, s_dump_argv[i]);
|
||
strcat(sCmd,"&");
|
||
printf("Dump [CMD:%s] [PID:%d] [Unix Num:%d] cmd: %s\n",s_criu_cmd,i_criu_pid, i_unix_num, sCmd);
|
||
system(sCmd);
|
||
|
||
snprintf(sCpCMd, sizeof sCpCMd, "cp /tmp/1 /tmp/criu/%s/", s_criu_cmd);
|
||
system(sCpCMd);
|
||
return 0;
|
||
}
|
||
|
||
int CMigrateApp::Restore()
|
||
{
|
||
char sCmd[2048] = {0};
|
||
char sCpCmd[64] = {0};
|
||
if(0 >= i_criu_pid)
|
||
{
|
||
printf("Error Pid! Restore neet to right dump!\n");
|
||
return -1;
|
||
}
|
||
for (int i = 0; s_restore_argv[i] != NULL; i++)
|
||
sprintf(sCmd, "%s %s", sCmd, s_restore_argv[i]);
|
||
strcat(sCmd,"&");
|
||
/* 进程有几个socket就创建几次 */
|
||
for (int i = 0; i < i_unix_num; i++)
|
||
socket(AF_UNIX, SOCK_STREAM, 0);
|
||
/* 将该进程转储时的/tmp/1 复制到指定路径下 */
|
||
snprintf(sCpCmd, sizeof sCpCmd, "cp /tmp/criu/%s/1 /tmp/1", s_criu_cmd);
|
||
system(sCpCmd);
|
||
system("chmod 644 /tmp/1");
|
||
/* 恢复 */
|
||
printf("Restore [CMD:%s] [Unix Num:%d] cmd: %s\n",s_criu_cmd, i_unix_num, sCmd);
|
||
system(sCmd);
|
||
return 0;
|
||
}
|
||
|
||
int CMigrateApp::GetPidByCMD(char *sCMD)
|
||
{
|
||
char cPid[62] = {0};
|
||
char sSeekCmd[64] = {0};
|
||
FILE *fPid = NULL;
|
||
int iPid = -1;
|
||
sprintf(sSeekCmd, "pgrep %s | head -1",sCMD);
|
||
fPid = popen(sSeekCmd, "r");
|
||
if(NULL == fPid)
|
||
return -1;
|
||
fgets(cPid, sizeof(cPid), fPid);
|
||
iPid = atoi(cPid);
|
||
// printf("SeekCmd: %s, Pid: %d, cPid: %s\n",sSeekCmd,iPid,cPid);
|
||
return iPid;
|
||
}
|
||
void CMigrateApp::GetCMDByPid(int iPid, char *sCMD)
|
||
{
|
||
FILE *fcmd = NULL;
|
||
char sGetCMD[64] = {0};
|
||
snprintf(sGetCMD, sizeof sGetCMD, "grep 'Name' /proc/%d/status | awk '{print $2}'", iPid);
|
||
fcmd = popen(sGetCMD, "r");
|
||
fgets(sCMD, sizeof(sCMD), fcmd);
|
||
}
|
||
int CMigrateApp::SetPidByCMD(char *sCMD)
|
||
{
|
||
char cPid[62] = {0};
|
||
char sSeekCmd[64] = {0};
|
||
FILE *fPid = NULL;
|
||
int iPid = -1;
|
||
sprintf(sSeekCmd, "pgrep %s | head -1", sCMD);
|
||
fPid = popen(sSeekCmd, "r");
|
||
if(NULL == fPid)
|
||
return -1;
|
||
fgets(cPid, sizeof(cPid), fPid);
|
||
iPid = atoi(cPid);
|
||
|
||
i_criu_pid = atoi(cPid);
|
||
snprintf(s_criu_pid, sizeof s_criu_pid, "%d", iPid);
|
||
// strcpy(s_criu_pid, cPid);
|
||
pclose(fPid);
|
||
//printf("SeekCmd: %s, Pid: %d, cPid: %s\n",sSeekCmd,iPid,cPid);
|
||
return iPid;
|
||
}
|
||
int CMigrateApp::SetUnixByPid(int iPid)
|
||
{
|
||
char sUnixStatCmd[64] = {0};
|
||
char sNowCriuUnixCmd[64] = {0};
|
||
char sNosRestoreUnixCmd[64] = {0};
|
||
FILE *fpUnixStat;
|
||
int iFdNum = 3;
|
||
char tmp[2048]; //存储每一行输出
|
||
unsigned int iNode;
|
||
int iSumUnix = 0;
|
||
// int i = 0;
|
||
|
||
if( iPid <=0 )
|
||
return 0;
|
||
|
||
sprintf(sUnixStatCmd, "cat /proc/%d/net/unix | awk '{print $7}'", iPid);
|
||
fpUnixStat = popen(sUnixStatCmd, "r");
|
||
if (fpUnixStat == NULL) {
|
||
return -1;
|
||
}
|
||
// 跳过第一条无效数据
|
||
fgets(tmp, sizeof(tmp) - 1, fpUnixStat);
|
||
// i = 0;
|
||
// 输出检查状态
|
||
// printf("check unix ..");
|
||
// fflush(stdout);
|
||
while (fgets(tmp, sizeof(tmp) - 1, fpUnixStat)) {
|
||
// 输出检查状态
|
||
// switch(i++){
|
||
// case 1:
|
||
// printf(".");
|
||
// fflush(stdout);
|
||
// break;
|
||
// case 5:
|
||
// printf("\b");
|
||
// fflush(stdout);
|
||
// break;
|
||
// case 10:
|
||
// i=0;
|
||
// default:
|
||
// break;
|
||
// };
|
||
sscanf(tmp, "%u", &iNode);
|
||
if(!isUnixActivity(iNode,s_criu_cmd))
|
||
continue;
|
||
if(iNode==0)
|
||
continue;
|
||
// printf("iPid: %d, Unix iNode: %d, is activity.\n", iPid,iNode);
|
||
snprintf(sNowCriuUnixCmd, sizeof(sNowCriuUnixCmd), "--ext-unix-sk=%u ",
|
||
(unsigned int)iNode);
|
||
snprintf(sNowCriuUnixCmd, sizeof(sNowCriuUnixCmd), "--external unix[%u] ",
|
||
(unsigned int)iNode);
|
||
snprintf(sNosRestoreUnixCmd, sizeof sNosRestoreUnixCmd, "--inherit-fd fd[%d]:socket:[%u] ",
|
||
iFdNum++,(unsigned int)iNode);
|
||
strcat(s_external_sk_ino, sNowCriuUnixCmd);
|
||
strcat(s_inh_unixsk_arg, sNosRestoreUnixCmd);
|
||
iSumUnix++;
|
||
}
|
||
// printf("\r"); // 清空 check unix 状态字符打印
|
||
pclose(fpUnixStat);
|
||
return iSumUnix;
|
||
}
|
||
int CMigrateApp::isUnixActivity(unsigned int iNode, char * cPid)
|
||
{
|
||
char sCheckUnixCmd[64] = {0};
|
||
FILE *fpCheckUnix;
|
||
char tmp[2048];
|
||
// 检查当前 unix socket 是否处于活跃状态
|
||
sprintf(sCheckUnixCmd, "netstat -xap | grep %s | grep %u", cPid, iNode);
|
||
fpCheckUnix = popen(sCheckUnixCmd, "r");
|
||
if (fpCheckUnix == NULL) {
|
||
return -1;
|
||
}
|
||
memset(tmp, 0x0, sizeof(tmp));
|
||
fgets(tmp, sizeof(tmp) - 1, fpCheckUnix);
|
||
pclose(fpCheckUnix);
|
||
printf("Check Cmd: %s, Rst len: %d", sCheckUnixCmd, (int)strlen(tmp));
|
||
fflush(stdout);
|
||
printf("%40s\r"," "); // 使用空格清理脏字符并回退光标至行首
|
||
fflush(stdout);
|
||
if(strlen(tmp)>1)
|
||
return 1;
|
||
return 0;
|
||
}
|
||
int CMigrateApp::GetPid()
|
||
{
|
||
return i_criu_pid;
|
||
}
|
||
int CMigrateApp::GetUnixNum()
|
||
{
|
||
return i_unix_num;
|
||
}
|
||
char *CMigrateApp::GetCMD()
|
||
{
|
||
return s_criu_cmd;
|
||
}
|
||
char *CMigrateApp::GetImgDir()
|
||
{
|
||
return s_criu_img;
|
||
}
|
||
|
||
void CMigrateApp::Mkdir_safe(char *dirname, int mode)
|
||
{
|
||
if (mkdir(dirname, mode) == -1 && errno != EEXIST)
|
||
printf("Success to mkdir dirname=%s mode=0x%x\n", dirname, mode);
|
||
else
|
||
printf("Fail to make dir %s", dirname);
|
||
}
|
||
|
||
// 自定义对象比较函数(根据unix个数排序)
|
||
bool cmp(CMigrateApp a, CMigrateApp b)
|
||
{
|
||
return a.GetUnixNum() < b.GetUnixNum();
|
||
}
|
||
void MigrateMySelf()
|
||
{
|
||
char sRestoreCmd[128] = {0};
|
||
CMigrateApp CMigrateMySelf(NULL, getpid());
|
||
snprintf(sRestoreCmd, sizeof sRestoreCmd, "criu restore -D %s -j", CMigrateMySelf.GetImgDir());
|
||
printf("Run cmd when restore: %s\n", sRestoreCmd);
|
||
CMigrateMySelf.Checkout();
|
||
}
|
||
|
||
int main()
|
||
{
|
||
char piggie_cmd[21][64] = {
|
||
/* create ipc unix socket */
|
||
"arpd", "dhcp", "lldpd", "ospfd", "ripd", "snmptrap", "rmon", "ripngd", "mstp", "lag","master","zebra","pidmonitor",
|
||
"ntpclient", "snmpd", "udpsvd", "brctl", "l2monitor", "vtysh", "configmanage", "udhcpd"};
|
||
std::vector <CMigrateApp> vMigrateApps;
|
||
|
||
// 清空进程检查点数据文件夹,避免遗留文件影响进程恢复
|
||
system("rm -rf /tmp/criu/*");
|
||
|
||
for(int i = 0; i < 21; i++)
|
||
{
|
||
CMigrateApp iC(piggie_cmd[i]);
|
||
vMigrateApps.push_back(iC);
|
||
// printf("%s [PID %d] will be push back, vector size: %d\n", iC.GetCMD(),iC.GetPid(), (int)vMigrateApps.size());
|
||
}
|
||
|
||
printf("-----------------------after sort-----------------------\n");
|
||
for(auto v : vMigrateApps)
|
||
{
|
||
printf("[%s:%d] has %d unix socket\n", v.GetCMD(), v.GetPid(), v.GetUnixNum());
|
||
}
|
||
// sort(vMigrateApps.begin(), vMigrateApps.end(),cmp);
|
||
printf("-------------------------- dump -------------------------\n");
|
||
for(auto v : vMigrateApps)
|
||
{
|
||
v.Checkout();
|
||
}
|
||
for(int j=10;j>0;j--)
|
||
{
|
||
printf("Waiting for start restore....%d\n",j);
|
||
if(j==5)
|
||
MigrateMySelf();
|
||
sleep(1);
|
||
}
|
||
|
||
printf("Enter any character to continue\n");
|
||
getchar();
|
||
printf("------------------------ restore ------------------------\n");
|
||
for(auto v : vMigrateApps)
|
||
{
|
||
if(v.GetPid()<=0)
|
||
{
|
||
printf("failed to dump, jump %s.\n", v.GetCMD());
|
||
continue;
|
||
}
|
||
v.Restore();
|
||
}
|
||
return 0;
|
||
}
|