首页 > 其他分享 >龙芯3A6000+loongnix20.6操作系统安装idea社区版2024和docker

龙芯3A6000+loongnix20.6操作系统安装idea社区版2024和docker

时间:2024-09-26 17:24:12浏览次数:1  
标签:return 龙芯 root void idea path array NULL 3A6000

龙芯3A6000+loongnix20.6操作系统安装idea社区版和docker

1.搭建目标:

安装jdk8
安装idea社区版-2024(需要jdk17)
安装docker(可选)
配置docker自动补全(可选)
如何使用docker拉取镜像(可选)

2. 配置说明

主机:中科云3A6000NUC
操作系统:loonignix-20.6

image

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替换为当前的用户主目录的路径
image

尝试执行启动脚本,如果可以启动,则证明修改正确
./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项目
image
然后设置刚才的maven配置文件作为当前项目的配置文件
image

打开项目的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,然后鼠标右键复制
image

接着使用鼠标打开文件管理器,打开目录:/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
image
然后保存,再重新打开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
image

然后添加一行配置,就是你刚才放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,然后回车
image
下图是搜到的仓库页面,点击左边的标签
image
找到你想要的版本,并点击右侧的下载按钮
image
这里会弹出一个框,右边下拉有两个选项,一个是按标签下载,一个是按摘要下载,我们就选第一个按标签下载
image
然后如下图点击按钮,自动复制命令到剪切板,然后直接粘贴到kconsole执行就行了,有时候一次可能不成功,需要多试几次
image

6.附录

6.1 jdk17

下载适用于龙芯处理器的jdk17,该版本支持4.18-4.19内核,否则需要手动拉取龙芯的jdk代码仓库进行编译,地址可以去loongnix官网自行查找

打开浏览器找到loongnix官网 http://www.loongnix.cn/zh/

image

找到java发布页 http://www.loongnix.cn/zh/api/java/

image

点击右侧的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

相关文章

  • IDEA调试的时候怎么确认一个代理对象是cglib动态代理实现的还是jdk动态代理实现的
    在调试过程中,判断一个代理对象是通过CGLIB实现的还是通过JDK动态代理实现的,可以从以下几个方面入手:###1.检查代理对象的实际类型在IntelliJIDEA中,你可以使用调试工具查看对象的实际类型。JDK动态代理和CGLIB动态代理在对象类型上有显著区别:-**JDK动态代理**:代理对......
  • IDEA 右键菜单注册表
    IDEA右键菜单-注册.regWindowsRegistryEditorVersion5.00[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\*\shell\OpenwithIDEA]"Icon"="D:\\ProgramFiles\\ideaIU-2023.3.8.win\\bin\\idea64.exe"@="EditwithIDEA"[HKEY_LO......
  • IDEA如何查看每一行代码的提交记录(人员,时间)
    前言我们在使用IDEA开发时,一般需要使用git来管理我们的代码,而且大家协同开发。 有时候,我们在开发的时候,经常需要看一下当前的代码时谁开发的,除了看类上面的作者外,更精细的方式是看每一行代码的提交记录。 那么,我们该怎么查看呢?如何查看首先,我们需要保证我们的代码是有git......
  • idea怎么快速生成get set方法,快捷键是什么?
    idea怎么快速生成getset方法参考文章:IntelliJIDEA生成get/set方法的快捷键是什么1、生成某个getset方法alt+enter快捷键:alt+enter2.生成整个类或者某个getset方法alt+insert快捷键:alt+insert点击后,会出现下图弹窗,你可以多选或者单选这些属性对象,然后点击ok......
  • JetBrains IDEA 插件 AI Assistant 不可用 - 解决方案
    问题描述:如果购买了IDEA的AIAssistant插件,但是打开后不可用并有下面的提示:“很遗憾,AIAssistant目前在您所在的位置不可用”。Unfortunately,AIAssistantiscurrentlyunavailableinyourlocation. 1.打开网址https://account.jetbrains.com/profile-details......
  • IntelliJ IDEA远程开发:释放本地设备,打破资源与环境的限制
    文章目录IntelliJIDEA远程开发:释放本地设备,打破资源与环境的限制应用场景远程开发优势如何实现远程开发进入远程开发`RemoteDevelopment`选择远程开发方式配置SSH代理转发通过`DevContainer`创建开发容器公众号,感谢!IntelliJIDEA远程开发:释放本地设备,打破资源与环境的限......
  • gi集成idea
    IDEA集成Git&IDEA集成GiteeIDEA集成Git(本地库)首先配置Git忽略文件(1)创建忽略规则文件文件名称:xxxx.ignore(前缀名随便起,建议是git.ignore)这个文件的存放位置原则上在哪里都可以,为了便于让~/.gitconfig文件引用,建议也放在用户家目录下git.ignore文件模版内容如下#......
  • 我是如何开发一款支持IDEA、PyCharm、Android Sutdio 等JB全家桶的摸鱼插件的
    公众号「古时的风筝」,专注于后端技术,尤其是Java及周边生态。个人博客:www.moonkite.cn大家好,我是风筝前些天做了一款支持Jetbrains大部分IDE的摸鱼插件-一款IDE摸鱼插件,没想到出乎意料的没什么人用,当初说VsCode里面的养宠物的插件时,一大堆人问IDEA里有没有、PyChar......
  • IDEA更改远程git仓库地址
    前言我们在使用IDEA开发时,一般会配置好对应的git仓库,这样就比较容易对代码进行控制以及协同开发。 但有时候,我们远程的仓库地址由于这样那样的原因,需要迁移(这在爱折腾的企业是常有的事情)。那么,我们该如何在IDEA中更新远程仓库地址呢?如何设置首先,我们点击上方的【Git】按钮,打......
  • 将Tomcat整合到idea中
    1.随便打开一个项目最上面选择run选择EditConfigurations将Applicationserver设置成下载的Tomcat目录将Openbrower设置成Chrome(谷歌浏览器,没有的建议下一个)将下图设置成自动发布Updataresources(建议将程序运行成功后再来修改)2.新建一个项目(File->New->Pro......