龙芯3A6000+loongnix20.6操作系统安装idea社区版和docker
1.搭建目标:
安装jdk8
安装idea社区版-2024(需要jdk17)
安装docker(可选)
配置docker自动补全(可选)
如何使用docker拉取镜像(可选)
2. 配置说明
主机:中科云3A6000NUC
操作系统:loonignix-20.6
3. 安装jdk
3.1 安装jdk8
打开桌面的文件管理器,鼠标右键菜单选择在此处打开终端
# 切换到root用户,并输入root密码
su - root
# 安装jdk8
apt install openjdk-8-jdk
# 注销root用户会话,回到常用用户
exit
3.2 安装jdk17
下载地址可以查看 附录6.1
比如我当前登录用户是tellme,那么这个账号的主目录就是/home/tellme
# 切换到当前用户主目录
cd
# 在主目录创建jdk存放目录
mkdir ./program/java
# 切换工作目录到此
cd ./program/java
# 下载jdk17
wget http://ftp.loongnix.cn/Java/openjdk17/loongson17.11.21-fx-jdk17.0.12_7-linux-loongarch64.tar.gz
# 解压
tar -xzvf loongson17.11.21-fx-jdk17.0.12_7-linux-loongarch64.tar.gz
# 将解压后的文件夹重命名为jdk17
mv loongson17.11.21-fx-jdk17.0.12_7-linux-loongarch64 jdk17
# 测试一下jdk是否可用,使用java命令显示jdk版本号,正常显示则说明可以运行
./jdk17/bin/java -version
4 安装idea
4.1 下载并解压idea安装包
下载方式查看 附录6.2
# 切换到当前用户主目录
cd
# 切换到程序目录,若没有可以创建
cd ./program
# 下载idea社区版-linux-2024.1.4
wget https://download.jetbrains.com.cn/idea/ideaIC-2024.1.4.tar.gz
# 解压
tar -xzvf ideaIC-2024.1.4.tar.gz
# 重命名
mv ideaIC-2024.1.4 idea
4.2 配置启动jdk目录(修改idea启动脚本)
# 修改idea启动脚本
cd idea
# 使用vim编辑idea启动脚本,把JRE变量设置为jdk安装目录,如果不知道vim的使用方法,可以百度搜索 或 直接按下列命令顺序输入
vim ./idea.sh
修改下列内容:
JRE="/home/tellme/program/java/jdk17"
请把/home/tellme替换为当前的用户主目录的路径
尝试执行启动脚本,如果可以启动,则证明修改正确
./idea.sh
4.3 配置JNA本地化方法(修改idea启动脚本,否则每次启动会提示你缺少本地库)
设置maven本地仓库地址,并添加龙芯的maven仓库作为镜像仓库
我的配置文件如下(仅供参考):
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<!-- 本地仓库地址 -->
<localRepository>/home/tellme/repo/mavenRepo</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
<mirror>
<id>public</id>
<mirrorOf>central,jcenter</mirrorOf>
<name>public-aliyun</name>
<!-- 老版地址 莫名无法访问-->
<!-- <url>https://maven.aliyun.com/repository/public</url> -->
<!-- 解决无法访问的临时方案 -->
<url>https://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
<mirror>
<id>loongarch64</id>
<mirrorOf>*</mirrorOf>
<name>loongarch64</name>
<url>https://maven.loongnix.cn/loongarch/maven</url>
</mirror>
</mirrors>
</settings>
启动idea, 新建一个maven项目
然后设置刚才的maven配置文件作为当前项目的配置文件
打开项目的pom.xml,并引入jna的5.14.0版本库,如下:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.14.0</version>
</dependency>
将上述内容复制到dependencies节点下,然后重新点击idea的maven重新导入
成功引入依赖后,可以在idea左侧的项目目录视图下方查看引入的依赖库,找到jna的这个库,并找到如图所示的文件libjnidispatch.so,然后鼠标右键复制
接着使用鼠标打开文件管理器,打开目录:/home/tellme/program/idea/lib/jna
创建文件夹loongarch64,然后把文件libjnidispatch.so粘贴到这个文件夹
鼠标右键打开终端
cd /home/tellme/program/idea/bin
vim ./idea.sh
找到如下内容并修改
-Djna.boot.library.path=$IDE_HOME/lib/jna/loongarch64
如下图所示位置进行修改,将amd64修改为loongarch64
然后保存,再重新打开idea,不会再提示你无法访问本地方法了
4.4 编译fsNotifier
官方说明在这里,大致意思是fsnotifier的二进制文件只支持x86、AArch64,其他架构自己按文章说明编译,地址在这里
https://intellij-support.jetbrains.com/hc/en-us/articles/15268038418322-Compiling-File-Watcher
为防止这个地址以后变更或失效,我把jetbrains的这个内容贴这里
Compiling File Watcher
FollowNot yet followed by anyone
Avatar
Lejia Chen
Updated November 23, 2023 19:49
At the moment, JetBrains provides builds of fsnotifier binary only for x86-64 and AArch64 architectures. If you want to use it on some other platform, please compile it yourself:
Upgrade the IDE to version 2019.3 or newer.
Make sure you have C compiler and standard library headers/libraries packages installed (on Ubuntu, sudo apt install libc6-dev gcc should be enough).
Download all *.h and *.c files along with make.sh from <https://github.com/JetBrains/intellij-community/tree/master/native/fsNotifier/linux>, then execute make.sh in the directory with downloaded files, optionally setting $CC if clang is not available.
Copy the compiled fsnotifier binary into any location, preferably outside of the IDE installation directory.
Start the IDE and invoke Help | Edit Custom Properties action (or Configure | Edit Custom Properties from the welcome screen), add the following line, then restart the IDE:
`idea.filewatcher.executable.path = /path/to/fsnotifier`
You may need to adjust inotify(7) instances and watches limits.
https://github.com/JetBrains/intellij-community/tree/master/native/fsNotifier/linux
下载里面的文件,如果部分同学不方便访问github,我把fsnptifier需要编译的源代码贴到附录6.3里(代码量不多,但是因占篇幅较多所以放在附录中,截至此部分编写时为最新代码)
使用vim打开文件make.sh
在第3行"set -euo pipefail"前面加上CC="gcc",如下:
CC="gcc"
set -euo pipefail
然后执行./make.sh,就会得到文件fsnotifier,然后我们把这个文件放入idea的安装目录的lib文件夹中
# 切换到idea根目录
cd /home/tellme/program/idea
# 切换到lib文件夹
cd lib
# 创建fsnotifier文件夹并切换其为当前目录
mkdir fsnotifier
cd fsnotifier
# 复制fsnotifier文件到该目录,以我的编译目录为例
cp /home/tellme/code/cpp/fsnotifier/fsnotifier ./
启动idea,打开下图选项,会打开文件idea.properties
然后添加一行配置,就是你刚才放fsnotifier文件的位置的完整路径, 下图以我存放的路径为例
idea.filewatcher.executable.path = /home/tellme/program/idea/lib/fsnotifier/fsnotifier
然后就ok了,恭喜你,idea配置完成,可以安心使用了!
5 安装docker (可选)
5.1 安装docker
查看loongnix官网关于docker安装的说明
https://docs.loongnix.cn/loongnix/cloud/docker/manual.html
# 切换root用户
su - root
#安装docker社区版
apt install docker-ce -y
设置龙芯为镜像仓库,这步必须有,因为架构原因,很多镜像并不支持loongarch, 所以必须添加龙芯的镜像仓库作为镜像源,这里把阿里的镜像仓库设置为加速地址
在 /etc/docker/daemon.json 中追加以下内容 ,如果文件不存在请手动创建
{
"registry-mirrors": ["https://cr.loongnix.cn"]
}
重启 docker 以使配置生效
systemctl daemon-reload
# 重启docker
systemctl restart docker
# 设置开机自动启动
systemctl enable docker
5.2 docker命令补全
下载命令补全文件
cd /etc/bash_completion.d/
wget https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker
# 使当前终端生效
source /etc/bash_completion
5.3 如何使用docker拉取镜像
首先需要找到你想要使用的镜像,有两种方式。
以redis为例
通过浏览器访问龙芯的官方镜像库查找,地址:https://cr.loongnix.cn/search
由于很多官方镜像还不能在loongarch上跑,所以本文以龙芯的镜像仓库为准
打开网站后,搜索框输入redis,然后回车
下图是搜到的仓库页面,点击左边的标签
找到你想要的版本,并点击右侧的下载按钮
这里会弹出一个框,右边下拉有两个选项,一个是按标签下载,一个是按摘要下载,我们就选第一个按标签下载
然后如下图点击按钮,自动复制命令到剪切板,然后直接粘贴到kconsole执行就行了,有时候一次可能不成功,需要多试几次
6.附录
6.1 jdk17
下载适用于龙芯处理器的jdk17,该版本支持4.18-4.19内核,否则需要手动拉取龙芯的jdk代码仓库进行编译,地址可以去loongnix官网自行查找
打开浏览器找到loongnix官网 http://www.loongnix.cn/zh/
找到java发布页 http://www.loongnix.cn/zh/api/java/
点击右侧的jdk17后面的loongarch64下载链接进行下载
截至文章编写时版本为17.11.21
http://ftp.loongnix.cn/Java/openjdk17/loongson17.11.21-fx-jdk17.0.12_7-linux-loongarch64.tar.gz
6.2 idea-2024-社区版
可通过idea官网下载,官网下载地址:https://www.jetbrains.com/zh-cn/idea/download/?section=linux
6.3 fsNotifier源代码
下载地址:https://github.com/JetBrains/intellij-community/tree/master/native/fsNotifier/linux
共5个文件分别如下:
fsnotifier.h
inotify.c
main.c
make.sh
util.c
fsnotifier.h
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
#pragma once
#ifndef VERSION
#define VERSION "SNAPSHOT"
#endif
#define _DEFAULT_SOURCE
#define _FILE_OFFSET_BITS 64
#include <features.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
// messaging and logging
void message(const char *text);
enum { LOG_ERR = 0, LOG_WARNING = 1, LOG_INFO = 2 };
void userlog(int priority, const char* format, ...);
#define CHECK_NULL(p, r) if ((p) == NULL) { userlog(LOG_ERR, "out of memory"); return r; }
// variable-length array
typedef struct array_str array;
array* array_create(int initial_capacity);
int array_size(array* a);
void* array_push(array* a, void* element);
void* array_pop(array* a);
void array_put(array* a, int index, void* element);
void* array_get(array* a, int index);
void array_delete(array* a);
void array_delete_vs_data(array* a);
void array_delete_data(array* a);
// poor man's hash table
typedef struct table_str table;
table* table_create(int capacity);
void* table_put(table* t, int key, void* value);
void* table_get(table* t, int key);
void table_delete(table* t);
// inotify subsystem
enum {
ERR_IGNORE = -1,
ERR_CONTINUE = -2,
ERR_ABORT = -3,
ERR_MISSING = -4
};
bool init_inotify(void);
void set_inotify_callback(void (*callback)(const char *, uint32_t));
int get_inotify_fd(void);
int watch(const char* root, array* mounts);
void unwatch(int id);
bool process_inotify_input(void);
void close_inotify(void);
// reads one line from stream, trims trailing carriage return if any
// returns pointer to the internal buffer (will be overwritten on next call)
char* read_line(FILE* stream);
// path comparison
bool is_parent_path(const char* parent_path, const char* child_path);
inotify.c
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
#include "fsnotifier.h"
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <unistd.h>
#define WATCH_COUNT_NAME "/proc/sys/fs/inotify/max_user_watches"
#define DEFAULT_SUBDIR_COUNT 5
typedef struct watch_node_str {
int wd;
struct watch_node_str* parent;
array* kids;
unsigned int path_len;
char path[];
} watch_node;
static int inotify_fd = -1;
static int watch_count = 0;
static table* watches;
static bool limit_reached = false;
static void (* callback)(const char*, uint32_t) = NULL;
#define EVENT_SIZE (sizeof(struct inotify_event))
#define EVENT_BUF_LEN (2048 * (EVENT_SIZE + 16))
static char event_buf[EVENT_BUF_LEN];
static char path_buf[2 * PATH_MAX];
static void read_watch_descriptors_count(void);
static void watch_limit_reached(void);
bool init_inotify(void) {
inotify_fd = inotify_init();
if (inotify_fd < 0) {
int e = errno;
userlog(LOG_ERR, "inotify_init: %s", strerror(e));
if (e == EMFILE) {
message("inotify.instance.limit");
}
return false;
}
read_watch_descriptors_count();
if (watch_count <= 0) {
close(inotify_fd);
inotify_fd = -1;
return false;
}
userlog(LOG_INFO, "inotify watch descriptors: %d", watch_count);
watches = table_create(watch_count);
if (watches == NULL) {
userlog(LOG_ERR, "out of memory");
close(inotify_fd);
inotify_fd = -1;
return false;
}
return true;
}
static void read_watch_descriptors_count(void) {
FILE* f = fopen(WATCH_COUNT_NAME, "r");
if (f == NULL) {
userlog(LOG_ERR, "can't open %s: %s", WATCH_COUNT_NAME, strerror(errno));
return;
}
char* str = read_line(f);
if (str == NULL) {
userlog(LOG_ERR, "can't read from %s", WATCH_COUNT_NAME);
}
else {
watch_count = (int)strtol(str, NULL, 10);
}
fclose(f);
}
void set_inotify_callback(void (* _callback)(const char*, uint32_t)) {
callback = _callback;
}
int get_inotify_fd(void) {
return inotify_fd;
}
#define EVENT_MASK (IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_MOVE_SELF)
static int add_watch(unsigned int path_len, watch_node* parent) {
int wd = inotify_add_watch(inotify_fd, path_buf, EVENT_MASK);
if (wd < 0) {
if (errno == EACCES || errno == ENOENT) {
userlog(LOG_INFO, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
return ERR_IGNORE;
}
else if (errno == ENOSPC) {
userlog(LOG_WARNING, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
watch_limit_reached();
return ERR_CONTINUE;
}
else {
userlog(LOG_ERR, "inotify_add_watch(%s): %s", path_buf, strerror(errno));
return ERR_ABORT;
}
}
else {
userlog(LOG_INFO, "watching %s: %d", path_buf, wd);
}
watch_node* node = table_get(watches, wd);
if (node != NULL) {
if (node->wd != wd) {
userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s / %d", wd, path_buf, node->wd, node->path, watch_count);
return ERR_ABORT;
}
else if (strcmp(node->path, path_buf) != 0) {
char buf1[PATH_MAX], buf2[PATH_MAX];
const char* normalized1 = realpath(node->path, buf1);
const char* normalized2 = realpath(path_buf, buf2);
if (normalized1 == NULL || normalized2 == NULL || strcmp(normalized1, normalized2) != 0) {
userlog(LOG_ERR, "table error: collision at %d (new %s, existing %s)", wd, path_buf, node->path);
return ERR_ABORT;
}
else {
userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path_buf, node->path, normalized1);
return ERR_IGNORE;
}
}
return wd;
}
node = malloc(sizeof(watch_node) + path_len + 1);
CHECK_NULL(node, ERR_ABORT)
memcpy(node->path, path_buf, path_len + 1);
node->path_len = path_len;
node->wd = wd;
node->parent = parent;
node->kids = NULL;
if (parent != NULL) {
if (parent->kids == NULL) {
parent->kids = array_create(DEFAULT_SUBDIR_COUNT);
CHECK_NULL(parent->kids, ERR_ABORT)
}
CHECK_NULL(array_push(parent->kids, node), ERR_ABORT)
}
if (table_put(watches, wd, node) == NULL) {
userlog(LOG_ERR, "table error: unable to put (%d:%s)", wd, path_buf);
return ERR_ABORT;
}
return wd;
}
static void watch_limit_reached(void) {
if (!limit_reached) {
limit_reached = true;
message("inotify.watch.limit");
}
}
static void rm_watch(int wd, bool update_parent) {
watch_node* node = table_get(watches, wd);
if (node == NULL) {
return;
}
userlog(LOG_INFO, "unwatching %s: %d (%p)", node->path, node->wd, node);
if (inotify_rm_watch(inotify_fd, node->wd) < 0) {
userlog(LOG_INFO, "inotify_rm_watch(%d:%s): %s", node->wd, node->path, strerror(errno));
}
for (int i = 0; i < array_size(node->kids); i++) {
watch_node* kid = array_get(node->kids, i);
if (kid != NULL) {
rm_watch(kid->wd, false);
}
}
if (update_parent && node->parent != NULL) {
for (int i = 0; i < array_size(node->parent->kids); i++) {
if (array_get(node->parent->kids, i) == node) {
array_put(node->parent->kids, i, NULL);
break;
}
}
}
array_delete(node->kids);
free(node);
table_put(watches, wd, NULL);
}
static int walk_tree(unsigned int path_len, watch_node* parent, bool recursive, array* mounts) {
for (int j = 0; j < array_size(mounts); j++) {
char* mount = array_get(mounts, j);
if (strncmp(path_buf, mount, strlen(mount)) == 0) {
userlog(LOG_INFO, "watch path '%s' crossed mount point '%s' - skipping", path_buf, mount);
return ERR_IGNORE;
}
}
DIR* dir = NULL;
if (recursive) {
if ((dir = opendir(path_buf)) == NULL) {
if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) {
userlog(LOG_INFO, "opendir(%s): %d", path_buf, errno);
return ERR_IGNORE;
}
else {
userlog(LOG_ERR, "opendir(%s): %s", path_buf, strerror(errno));
return ERR_CONTINUE;
}
}
}
int id = add_watch(path_len, parent);
if (dir == NULL) {
return id;
}
else if (id < 0) {
closedir(dir);
return id;
}
path_buf[path_len] = '/';
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_DIR) {
continue;
}
unsigned int name_len = strlen(entry->d_name);
memcpy(path_buf + path_len + 1, entry->d_name, name_len + 1);
if (entry->d_type == DT_UNKNOWN) {
struct stat st;
if (stat(path_buf, &st) != 0) {
userlog(LOG_INFO, "(DT_UNKNOWN) stat(%s): %d", path_buf, errno);
continue;
}
if (!S_ISDIR(st.st_mode)) {
continue;
}
}
int subdir_id = walk_tree(path_len + 1 + name_len, table_get(watches, id), recursive, mounts);
if (subdir_id < 0 && subdir_id != ERR_IGNORE) {
rm_watch(id, true);
id = subdir_id;
break;
}
}
closedir(dir);
return id;
}
int watch(const char* root, array* mounts) {
bool recursive = true;
if (root[0] == '|') {
root++;
recursive = false;
}
size_t path_len = strlen(root);
if (root[path_len - 1] == '/') {
--path_len;
}
struct stat st;
if (stat(root, &st) != 0) {
if (errno == ENOENT) {
return ERR_MISSING;
}
else if (errno == EACCES || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOTDIR) {
userlog(LOG_INFO, "stat(%s): %s", root, strerror(errno));
return ERR_CONTINUE;
}
else {
userlog(LOG_ERR, "stat(%s): %s", root, strerror(errno));
return ERR_ABORT;
}
}
if (S_ISREG(st.st_mode)) {
recursive = false;
}
else if (!S_ISDIR(st.st_mode)) {
userlog(LOG_WARNING, "unexpected node type: %s, %d", root, st.st_mode);
return ERR_IGNORE;
}
memcpy(path_buf, root, path_len);
path_buf[path_len] = '\0';
return walk_tree(path_len, NULL, recursive, mounts);
}
void unwatch(int id) {
rm_watch(id, true);
}
static bool process_inotify_event(struct inotify_event* event) {
watch_node* node = table_get(watches, event->wd);
if (node == NULL) {
return true;
}
bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR;
userlog(LOG_INFO, "inotify: wd=%d mask=%d dir=%d name=%s", event->wd, event->mask & (~IN_ISDIR), is_dir, node->path);
unsigned int path_len = node->path_len;
memcpy(path_buf, node->path, path_len + 1);
if (event->len > 0) {
path_buf[path_len] = '/';
unsigned int name_len = strlen(event->name);
memcpy(path_buf + path_len + 1, event->name, name_len + 1);
path_len += name_len + 1;
}
if (callback != NULL) {
(*callback)(path_buf, event->mask);
}
if (is_dir && event->mask & (IN_CREATE | IN_MOVED_TO)) {
int result = walk_tree(path_len, node, true, NULL);
if (result < 0 && result != ERR_IGNORE && result != ERR_CONTINUE) {
return false;
}
}
if (is_dir && event->mask & (IN_DELETE | IN_MOVED_FROM)) {
for (int i = 0; i < array_size(node->kids); i++) {
watch_node* kid = array_get(node->kids, i);
if (kid != NULL && strncmp(path_buf, kid->path, kid->path_len) == 0) {
rm_watch(kid->wd, false);
array_put(node->kids, i, NULL);
break;
}
}
}
return true;
}
bool process_inotify_input(void) {
ssize_t len = read(inotify_fd, event_buf, EVENT_BUF_LEN);
if (len < 0) {
userlog(LOG_ERR, "read: %s", strerror(errno));
return false;
}
ssize_t i = 0;
while (i < len) {
struct inotify_event *event = (struct inotify_event *) &event_buf[i];
i += (int)EVENT_SIZE + event->len;
if (event->mask & IN_IGNORED) {
continue;
}
if (event->mask & IN_Q_OVERFLOW) {
userlog(LOG_INFO, "event queue overflow");
continue;
}
if (!process_inotify_event(event)) {
return false;
}
}
return true;
}
void close_inotify(void) {
if (watches != NULL) {
table_delete(watches);
}
if (inotify_fd >= 0) {
close(inotify_fd);
}
}
main.c
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
#include "fsnotifier.h"
#include <errno.h>
#include <limits.h>
#include <mntent.h>
#include <paths.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <unistd.h>
#define USAGE_MSG \
"fsnotifier - IntelliJ Platform companion program for watching and reporting file and directory structure modifications.\n\n" \
"Use 'fsnotifier --selftest' to perform some self-diagnostics (output will be printed to console).\n"
#define HELP_MSG \
"Try 'fsnotifier --help' for more information.\n"
#define MISSING_ROOT_TIMEOUT 1
#define UNFLATTEN(root) (root[0] == '|' ? root + 1 : root)
typedef struct {
char* path;
int id; // negative value means missing root
} watch_root;
static array* roots = NULL;
static bool self_test = false;
static void run_self_test(void);
static bool main_loop(void);
static int read_input(void);
static bool update_roots(array* new_roots);
static void unregister_roots(void);
static bool register_roots(array* new_roots, array* unwatchable, array* mounts);
static array* unwatchable_mounts(void);
static void inotify_callback(const char* path, uint32_t event);
static void report_event(const char* event, const char* path);
static void output(const char* line, bool flush);
static void check_missing_roots(void);
static void check_root_removal(const char*);
int main(int argc, char** argv) {
if (argc > 1) {
if (strcmp(argv[1], "--help") == 0) {
printf(USAGE_MSG);
return 0;
}
else if (strcmp(argv[1], "--version") == 0) {
printf("fsnotifier " VERSION "\n");
return 0;
}
else if (strcmp(argv[1], "--selftest") == 0) {
self_test = true;
}
else {
printf("unrecognized option: %s\n", argv[1]);
printf(HELP_MSG);
return 1;
}
}
userlog(LOG_INFO, "fsnotifier self-test mode (v." VERSION ")");
setvbuf(stdin, NULL, _IONBF, 0);
int rv = 0;
roots = array_create(20);
if (roots != NULL && init_inotify()) {
set_inotify_callback(&inotify_callback);
if (self_test) {
run_self_test();
}
else if (!main_loop()) {
rv = 3;
}
unregister_roots();
}
else {
output("GIVEUP", true);
rv = 2;
}
close_inotify();
array_delete(roots);
userlog(LOG_INFO, "finished (%d)", rv);
return rv;
}
void message(const char *text) {
output("MESSAGE", false);
output(text, true);
}
void userlog(int level, const char* format, ...) {
va_list ap;
if (self_test) {
fputs(level == LOG_ERR ? "[E] " : level == LOG_WARNING ? "[W] " : "[I] ", stdout);
va_start(ap, format);
vfprintf(stdout, format, ap);
va_end(ap);
fputc('\n', stdout);
}
else if (level <= LOG_WARNING) {
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
}
}
static void run_self_test(void) {
array* test_roots = array_create(1);
char* cwd = malloc(PATH_MAX);
if (getcwd(cwd, PATH_MAX) == NULL) {
strncpy(cwd, ".", PATH_MAX);
}
array_push(test_roots, cwd);
update_roots(test_roots);
}
static bool main_loop(void) {
int input_fd = fileno(stdin), inotify_fd = get_inotify_fd();
int nfds = (inotify_fd > input_fd ? inotify_fd : input_fd) + 1;
fd_set rfds;
struct timeval timeout;
while (true) {
usleep(50000);
FD_ZERO(&rfds);
FD_SET(input_fd, &rfds);
FD_SET(inotify_fd, &rfds);
timeout = (struct timeval){MISSING_ROOT_TIMEOUT, 0};
if (select(nfds, &rfds, NULL, NULL, &timeout) < 0) {
if (errno != EINTR) {
userlog(LOG_ERR, "select: %s", strerror(errno));
return false;
}
}
else if (FD_ISSET(input_fd, &rfds)) {
int result = read_input();
if (result == 0) return true;
else if (result != ERR_CONTINUE) return false;
}
else if (FD_ISSET(inotify_fd, &rfds)) {
if (!process_inotify_input()) return false;
}
else {
check_missing_roots();
}
}
}
static int read_input(void) {
char* line = read_line(stdin);
if (line == NULL || strcmp(line, "EXIT") == 0) {
return 0;
}
else if (strcmp(line, "ROOTS") == 0) {
array* new_roots = array_create(20);
CHECK_NULL(new_roots, ERR_ABORT)
while (true) {
line = read_line(stdin);
if (line == NULL || strlen(line) == 0) {
return 0;
}
else if (strcmp(line, "#") == 0) {
break;
}
else {
size_t l = strlen(line);
if (l > 1 && line[l-1] == '/') line[l-1] = '\0';
CHECK_NULL(array_push(new_roots, strdup(line)), ERR_ABORT)
}
}
return update_roots(new_roots) ? ERR_CONTINUE : ERR_ABORT;
}
else {
userlog(LOG_WARNING, "unrecognised command: '%s'", line);
return ERR_CONTINUE;
}
}
static bool update_roots(array* new_roots) {
userlog(LOG_INFO, "updating roots (curr:%d, new:%d)", array_size(roots), array_size(new_roots));
unregister_roots();
if (array_size(new_roots) == 0) {
output("UNWATCHEABLE\n#", true);
array_delete(new_roots);
return true;
}
if (array_size(new_roots) == 1 && strcmp(array_get(new_roots, 0), "/") == 0) { // refusing to watch the entire tree
output("UNWATCHEABLE\n/\n#", true);
array_delete_vs_data(new_roots);
return true;
}
array* mounts = unwatchable_mounts();
if (mounts == NULL) {
return false;
}
array* unwatchable = array_create(20);
if (!register_roots(new_roots, unwatchable, mounts)) {
return false;
}
output("UNWATCHEABLE", false);
for (int i = 0; i < array_size(unwatchable); i++) {
output(array_get(unwatchable, i), false);
}
output("#", true);
array_delete_vs_data(unwatchable);
array_delete_vs_data(mounts);
array_delete_vs_data(new_roots);
return true;
}
static void unregister_roots(void) {
watch_root* root;
while ((root = array_pop(roots)) != NULL) {
userlog(LOG_INFO, "unregistering root: %s", root->path);
unwatch(root->id);
free(root->path);
free(root);
}
}
static bool register_roots(array* new_roots, array* unwatchable, array* mounts) {
for (int i = 0; i < array_size(new_roots); i++) {
char* new_root = array_get(new_roots, i);
char* unflattened = UNFLATTEN(new_root);
userlog(LOG_INFO, "registering root: %s", new_root);
if (unflattened[0] != '/') {
userlog(LOG_WARNING, "invalid root: %s", new_root);
continue;
}
array* inner_mounts = array_create(5);
CHECK_NULL(inner_mounts, false)
bool skip = false;
for (int j = 0; j < array_size(mounts); j++) {
char* mount = array_get(mounts, j);
if (is_parent_path(mount, unflattened)) {
userlog(LOG_INFO, "watch root '%s' is under mount point '%s' - skipping", unflattened, mount);
CHECK_NULL(array_push(unwatchable, strdup(unflattened)), false)
skip = true;
break;
}
else if (is_parent_path(unflattened, mount)) {
userlog(LOG_INFO, "watch root '%s' contains mount point '%s' - partial watch", unflattened, mount);
char* copy = strdup(mount);
CHECK_NULL(array_push(unwatchable, copy), false)
CHECK_NULL(array_push(inner_mounts, copy), false)
}
}
if (skip) {
continue;
}
int id = watch(new_root, inner_mounts);
array_delete(inner_mounts);
if (id >= 0 || id == ERR_MISSING) {
watch_root* root = malloc(sizeof(watch_root));
CHECK_NULL(root, false)
root->id = id;
root->path = strdup(new_root);
CHECK_NULL(root->path, false)
CHECK_NULL(array_push(roots, root), false)
}
else if (id == ERR_ABORT) {
return false;
}
else if (id != ERR_IGNORE) {
userlog(LOG_WARNING, "watch root '%s' cannot be watched: %d", unflattened, id);
CHECK_NULL(array_push(unwatchable, strdup(unflattened)), false)
}
}
return true;
}
static bool is_watchable(const char* fs) {
// do not watch special and network filesystems
return !(strncmp(fs, "dev", 3) == 0 || strcmp(fs, "proc") == 0 || strcmp(fs, "sysfs") == 0 || strcmp(fs, MNTTYPE_SWAP) == 0 ||
strcmp(fs, "cifs") == 0 || strcmp(fs, MNTTYPE_NFS) == 0 || strcmp(fs, "9p") == 0 ||
(strncmp(fs, "fuse", 4) == 0 && strcmp(fs + 4, "blk") != 0 && strcmp(fs + 4, ".osxfs") != 0));
}
static array* unwatchable_mounts(void) {
FILE* mtab = setmntent(_PATH_MOUNTED, "r");
if (mtab == NULL && errno == ENOENT) {
mtab = setmntent("/proc/mounts", "r");
}
if (mtab == NULL) {
userlog(LOG_ERR, "cannot open " _PATH_MOUNTED);
return NULL;
}
array* mounts = array_create(20);
CHECK_NULL(mounts, NULL)
struct mntent* ent;
while ((ent = getmntent(mtab)) != NULL) {
userlog(LOG_INFO, "mtab: %s : %s", ent->mnt_dir, ent->mnt_type);
if (strcmp(ent->mnt_type, MNTTYPE_IGNORE) != 0 && !is_watchable(ent->mnt_type)) {
CHECK_NULL(array_push(mounts, strdup(ent->mnt_dir)), NULL)
}
}
endmntent(mtab);
return mounts;
}
static void inotify_callback(const char* path, uint32_t event) {
if (event & (IN_CREATE | IN_MOVED_TO)) {
report_event("CREATE", path);
report_event("CHANGE", path);
}
else if (event & IN_MODIFY) {
report_event("CHANGE", path);
}
else if (event & IN_ATTRIB) {
report_event("STATS", path);
}
else if (event & (IN_DELETE | IN_MOVED_FROM)) {
report_event("DELETE", path);
}
if (event & (IN_DELETE_SELF | IN_MOVE_SELF)) {
check_root_removal(path);
}
else if (event & IN_UNMOUNT) {
output("RESET", true);
}
}
static void report_event(const char* event, const char* path) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
char* copy = path, *p;
for (p = copy; *p != '\0'; ++p) {
if (*p == '\n') {
if (copy == path) {
copy = strdup(path);
p = copy + (p - path);
}
*p = '\0';
}
}
#pragma clang diagnostic pop
fputs(event, stdout);
fputc('\n', stdout);
fwrite(copy, (p - copy), 1, stdout);
fputc('\n', stdout);
fflush(stdout);
if (copy != path) {
free(copy);
}
}
static void output(const char* line, bool flush) {
fputs(line, stdout);
fputc('\n', stdout);
if (flush) {
fflush(stdout);
}
}
static void check_missing_roots(void) {
struct stat st;
for (int i = 0; i < array_size(roots); i++) {
watch_root* root = array_get(roots, i);
if (root->id < 0) {
char* unflattened = UNFLATTEN(root->path);
if (stat(unflattened, &st) == 0) {
root->id = watch(root->path, NULL);
userlog(LOG_INFO, "root restored: %s\n", root->path);
report_event("CREATE", unflattened);
report_event("CHANGE", unflattened);
}
}
}
}
static void check_root_removal(const char* path) {
for (int i = 0; i < array_size(roots); i++) {
watch_root* root = array_get(roots, i);
if (root->id >= 0 && strcmp(path, UNFLATTEN(root->path)) == 0) {
unwatch(root->id);
root->id = -1;
userlog(LOG_INFO, "root deleted: %s\n", root->path);
report_event("DELETE", path);
}
}
}
make.sh
#!/bin/bash
# Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
set -euo pipefail
VER="${1:-}"
if [ -z "${VER:-}" ]; then
VER=$(date "+%Y%m%d.%H%M")
fi
rm -f fsnotifier
${CC:-clang} -O2 -Wall -Wextra -Wpedantic -D "VERSION=\"$VER\"" -std=c11 main.c inotify.c util.c -o fsnotifier
chmod 755 fsnotifier
# ensuring supported builds are compatible with RHEL/CentOS 7
MAX_GLIBC_VERSION="2.17"
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ] || [ "$ARCH" = "amd64" ] || [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
glibc_version="$(objdump -x fsnotifier | grep -o "GLIBC_.*" | sort | uniq | cut -d _ -f 2 | sort -V | tail -n 1)"
newest=$(printf "%s\n%s\n" "$MAX_GLIBC_VERSION" "$glibc_version" | sort -V | tail -n 1)
if [ "$newest" != "$MAX_GLIBC_VERSION" ]; then
echo "ERROR: fsnotifier uses glibc version $glibc_version which is newer than $MAX_GLIBC_VERSION"
exit 1
fi
fi
util.c
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
#include "fsnotifier.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REALLOC_FACTOR 2
struct array_str {
void** data;
int size;
int capacity;
};
static bool array_realloc(array* a) {
if (a->size == a->capacity) {
int new_cap = a->capacity * REALLOC_FACTOR;
void* new_ptr = realloc(a->data, sizeof(void*) * new_cap);
if (new_ptr == NULL) {
return false;
}
a->capacity = new_cap;
a->data = new_ptr;
}
return true;
}
array* array_create(int initial_capacity) {
array* a = calloc(1, sizeof(array));
if (a == NULL) {
return NULL;
}
a->data = calloc(initial_capacity, sizeof(void*));
if (a->data == NULL) {
free(a);
return NULL;
}
a->capacity = initial_capacity;
a->size = 0;
return a;
}
inline int array_size(array* a) {
return (a != NULL ? a->size : 0);
}
void* array_push(array* a, void* element) {
if (a == NULL || !array_realloc(a)) {
return NULL;
}
a->data[a->size++] = element;
return element;
}
void* array_pop(array* a) {
if (a != NULL && a->size > 0) {
return a->data[--a->size];
}
else {
return NULL;
}
}
void array_put(array* a, int index, void* element) {
if (a != NULL && index >=0 && index < a->capacity) {
a->data[index] = element;
if (a->size <= index) {
a->size = index + 1;
}
}
}
void* array_get(array* a, int index) {
if (a != NULL && index >= 0 && index < a->size) {
return a->data[index];
}
else {
return NULL;
}
}
void array_delete(array* a) {
if (a != NULL) {
free(a->data);
free(a);
}
}
void array_delete_vs_data(array* a) {
if (a != NULL) {
array_delete_data(a);
array_delete(a);
}
}
void array_delete_data(array* a) {
if (a != NULL) {
for (int i = 0; i < a->size; i++) {
if (a->data[i] != NULL) {
free(a->data[i]);
}
}
a->size = 0;
}
}
struct table_str {
void** data;
int capacity;
};
table* table_create(int capacity) {
table* t = calloc(1, sizeof(table));
if (t == NULL) {
return NULL;
}
t->data = calloc(capacity, sizeof(void*));
if (t->data == NULL) {
free(t);
return NULL;
}
t->capacity = capacity;
return t;
}
static inline int wrap(int key, table* t) {
return (t != NULL ? key % t->capacity : -1);
}
// todo: resolve collisions (?)
void* table_put(table* t, int key, void* value) {
int k = wrap(key, t);
if (k < 0 || (value != NULL && t->data[k] != NULL)) {
return NULL;
}
else {
return t->data[k] = value;
}
}
void* table_get(table* t, int key) {
int k = wrap(key, t);
if (k < 0) {
return NULL;
}
else {
return t->data[k];
}
}
void table_delete(table* t) {
if (t != NULL) {
free(t->data);
free(t);
}
}
#define INPUT_BUF_LEN 2048
static char input_buf[INPUT_BUF_LEN];
char* read_line(FILE* stream) {
char* result = fgets(input_buf, INPUT_BUF_LEN, stream);
if (result == NULL || feof(stream)) {
return NULL;
}
size_t pos = strlen(input_buf) - 1;
if (input_buf[pos] == '\n') {
input_buf[pos] = '\0';
}
return input_buf;
}
bool is_parent_path(const char* parent_path, const char* child_path) {
size_t parent_len = strlen(parent_path);
return strncmp(parent_path, child_path, parent_len) == 0 &&
(parent_len == strlen(child_path) || child_path[parent_len] == '/');
}
标签:return,龙芯,root,void,idea,path,array,NULL,3A6000
From: https://www.cnblogs.com/zeromi/p/18433816