0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElectronでNFSマウント操作UIを作ってみた

Posted at

背景・目的

本ページは、ElectronでNFSマウント操作UIを作成したときのメモになります。
ローカルの仮想環境(UTM + Ubuntu Desktop)を使って、仕組みや動作の検証を進めています。

実践

開発環境を用意する

  1. Ubuntuのページを開きます

  2. 「Download」ボタンをクリックします
    image.png

  3. 前回、インストールしたUTMを起動します

  4. 「新規仮想マシンを作成」をクリックします
    image.png

  5. 「仮想化」をクリックします
    image.png

  6. 「Linux」をクリックします
    image.png

  7. 「選択」をクリックし、ダウンロードした「ubuntu-24.04.2-live-server-amd64.iso」を選択します
    image.png

  8. 「続ける」をクリックします

  9. 下記を指定して「続ける」をクリックします

    • メモリ:4096MB
    • CPUコア数:2
  10. ストレージは、「64GiB」を指定して、「続ける」をクリックします

  11. 共有ディレクトリを指定して、「続ける」をクリックします

  12. 問題なければ、「保存」をクリックします

  13. 仮想マシンが表示されました。「再生」ボタンをクリックします
    image.png

  14. 「Try or Install Ubuntu Server」 を選びます

  15. SSHサーバをインストールします

  16. セットアップが進んでいき、「Reboot」します

  17. 「[FAILED] Failed unmounting cdrom.mount」が表示されます
    image.png

  18. CD/DVDの消去を選択します
    image.png

  19. ターミナルでEnterをクリックします

  20. しばらくするとログイン画面が表示されるので、設定したusernameとpasswordを設定します
    image.png

NFSサーバのインストール

  1. IPアドレスを確認します

    ip a
    
  2. ローカルPCからSSHします

     ssh <Username>@<UbuntuサーバのIP> -i ~/.ssh/秘密鍵
    
  3. NFSサーバのインストール

    sudo apt update
    sudo apt install nfs-kernel-server
    
  4. 共有ディレクトリを作成します

    ~$ ls -l /mnt
    total 0
    ~$ sudo mkdir -p /mnt/share
    ~$ sudo chown nobody:nogroup /mnt/share/
    ~$ ls -l /mnt
    total 4
    drwxr-xr-x 2 nobody nogroup 4096 Apr 20 10:33 share
    ~$ 
    
  5. ファイルを置きます

    ~$ echo "hello_nfs" | sudo tee /mnt/share/hello.txt
    hello_nfs
    ~$ 
    
  6. /etc/exports にエクスポート設定を追記します

    ~$ echo "/mnt/share 127.0.0.1(rw,sync,no_subtree_check)" | sudo tee -a /etc/exports
    /mnt/share 127.0.0.1(rw,sync,no_subtree_check)
    ~$ cat /etc/exports 
    # /etc/exports: the access control list for filesystems which may be exported
    #		to NFS clients.  See exports(5).
    #
    # Example for NFSv2 and NFSv3:
    # /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
    #
    # Example for NFSv4:
    # /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
    # /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
    #
    /mnt/share 127.0.0.1(rw,sync,no_subtree_check)
    ~$ 
    
  7. サーバ再起動します

    ~$ sudo exportfs -a
    ~$ sudo systemctl restart nfs-kernel-server
    $ 
    
  8. マウントをします

    ~$ sudo mkdir -p /mnt/test_mount
    ~$ sudo mount -t nfs 127.0.0.1:/mnt/share /mnt/test_mount
    ~$ 
    
  9. 見えました

    ~$ ls -l /mnt/test_mount/
    total 4
    -rw-r--r-- 1 root root 10 Apr 20 10:46 hello.txt
    ~$ cat /mnt/test_mount/hello.txt 
    hello_nfs
    ~$ 
    

Nodeのインストール

  1. nvmをインストールします

    ~$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
    
  2. シェルに反映します

    source ~/.bashrc 
    
  3. 最新のLTSバージョンのNode.jsをインストールします

    ~$ nvm install --lts
    Installing latest LTS version.
    Downloading and installing node v22.14.0...
    Downloading https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz...
    ###################################################################################################################################################################################################### 100.0%
    Computing checksum with sha256sum
    Checksums matched!
    Now using node v22.14.0 (npm v10.9.2)
    Creating default alias: default -> lts/* (-> v22.14.0)
    ~$ 
    
  4. インストールを確認します

    ~$ node -v
    v22.14.0
    ~$ npm -v
    10.9.2
    ~$ 
    

Ubuntu Desktopのインストール

  1. Ubuntu desktopをインストールします
    sudo apt install -y ubuntu-desktop
    

GUIライブラリの一括インストール

  1. apt updateを実行します
    $ sudo apt update
    [sudo] password for XXX: 
    Hit:1 http://security.ubuntu.com/ubuntu noble-security InRelease                           
    Hit:2 http://jp.archive.ubuntu.com/ubuntu noble InRelease                                  
    Get:3 http://jp.archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
    Hit:4 http://jp.archive.ubuntu.com/ubuntu noble-backports InRelease
    Fetched 126 kB in 3s (47.9 kB/s)
    Reading package lists... Done
    Building dependency tree... Done
    Reading state information... Done
    55 packages can be upgraded. Run 'apt list --upgradable' to see them.
    $ 
    
  2. ライブラリをインストールします
    sudo apt install -y \
      libatk1.0-0t64 \
      libgtk-3-0t64 \
      libxss1 \
      libasound2t64 \
      libnss3 \
      libx11-xcb1 \
      libxcomposite1 \
      libxdamage1 \
      libxrandr2 \
      libgbm1 \
      libpango-1.0-0 \
      libcairo2
    
    

Electron UIの構築

プロジェクトの作成

  1. 専用ディレクトリを作成します

    ~$ mkdir nfs-ui && cd nfs-ui
    
  2. 初期化します

    ~/nfs-ui$ npm init -y
    Wrote to /home/XXXX/nfs-ui/package.json:
    
    {
      "name": "nfs-ui",
      "version": "1.0.0",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "description": ""
    }
    
    
    
    ~/nfs-ui$ 
    
  3. electronをインストールします

    ~/nfs-ui$ npm install electron --save-dev
    npm warn deprecated boolean@3.2.0: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
    
    added 70 packages, and audited 71 packages in 26s
    
    17 packages are looking for funding
      run `npm fund` for details
    
    found 0 vulnerabilities
    ~/nfs-ui$ 
    

main.jsの作成

  1. main.jsを作成します
    const { app, BrowserWindow, ipcMain } = require('electron');
    const { exec } = require('child_process');
    
    function createWindow () {
      const win = new BrowserWindow({
        width: 400,
        height: 300,
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false
        }
      });
    
      win.loadFile('index.html');
    }
    
    app.whenReady().then(createWindow);
    
    // mount処理
    ipcMain.handle('mount-nfs', async () => {
      return new Promise((resolve, reject) => {
        exec('sudo mount -t nfs 127.0.0.1:/mnt/share /mnt/test_mount', (error, stdout, stderr) => {
          if (error) return reject(stderr);
          resolve('mounted');
        });
      });
    });
    
    // umount処理
    ipcMain.handle('umount-nfs', async () => {
      return new Promise((resolve, reject) => {
        exec('sudo umount /mnt/test_mount', (error, stdout, stderr) => {
          if (error) return reject(stderr);
          resolve('unmounted');
        });
      });
    });
    

index.htmlの作成

  1. index.htmlを作成します
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>NFS Control</title>
    </head>
    <body>
      <h1>NFS操作</h1>
      <button onclick="mount()">マウント</button>
      <button onclick="umount()">アンマウント</button>
      <p id="status"></p>
      <script src="renderer.js"></script>
    </body>
    </html>
    
    

renderer.jsの作成

  1. renderer.js を作成します
    const { ipcRenderer } = require('electron');
    
    async function mount() {
      try {
        await ipcRenderer.invoke('mount-nfs');
        document.getElementById('status').innerText = 'マウント成功';
      } catch (e) {
        document.getElementById('status').innerText = 'マウント失敗: ' + e;
      }
    }
    
    async function umount() {
      try {
        await ipcRenderer.invoke('umount-nfs');
        document.getElementById('status').innerText = 'アンマウント成功';
      } catch (e) {
        document.getElementById('status').innerText = 'アンマウント失敗: ' + e;
      }
    }
    

package.jsonの修正

  1. package.jsonに、"start": "electron ."main:main.jsを追加します
    ~/nfs-ui$ cat package.json 
    {
      "name": "nfs-ui",
      "version": "1.0.0",
      "main": "main.js",
      "scripts": {
        "start": "electron .",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "description": "",
      "devDependencies": {
        "electron": "^35.2.0"
      }
    }
    ~/nfs-ui$ 
    

chrome-sandbox に権限を設定

Electronは内部的にChrome(Chromium)を使っており、Linuxで特権昇格(setuid)機能によるサンドボックス保護を追加います。

  1. 権限を設定します
    ~/nfs-ui$ sudo chown root:root ./node_modules/electron/dist/chrome-sandbox
    ~/nfs-ui$ sudo chmod 4755 ./node_modules/electron/dist/chrome-sandbox
    

起動

  1. npm startで起動します

    npm start
    
  2. 画面が起動されました

  3. マウントをクリックします
    image.png

  4. アンマウントをクリックします
    image.png

ファイルの一覧を確認するように変更する

main.jsを修正

  1. 下記のように修正する
    const { app, BrowserWindow, ipcMain } = require('electron');
    const { exec } = require('child_process');
    const fs = require('fs');
    const path = require('path');
    
    function createWindow () {
      const win = new BrowserWindow({
        width: 600,
        height: 400,
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false, // 今回は簡略化のためfalse
        }
      });
    
      win.loadFile('index.html');
    }
    
    app.whenReady().then(createWindow);
    
    const mountPoint = '/mnt/test_mount';
    const nfsTarget = '127.0.0.1:/mnt/share';
    
    // ✅ mountしてファイル一覧を返す
    ipcMain.handle('mount-nfs', async () => {
      return new Promise((resolve, reject) => {
        exec(`sudo mount -t nfs ${nfsTarget} ${mountPoint}`, (error, stdout, stderr) => {
          if (error) return reject(stderr);
    
          try {
            const files = fs.readdirSync(mountPoint);
            resolve(files);
          } catch (err) {
            reject('マウントは成功しましたが、ファイル一覧取得に失敗しました');
          }
        });
      });
    });
    
    // ✅ umountのみ
    ipcMain.handle('umount-nfs', async () => {
      return new Promise((resolve, reject) => {
        exec(`sudo umount ${mountPoint}`, (error, stdout, stderr) => {
          if (error) return reject(stderr);
          resolve('アンマウント完了');
        });
      });
    });
    

index.html

  1. 下記のように修正します
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>NFS UI</title>
    </head>
    <body>
      <h2>NFS マウントテスト</h2>
      <button id="mount">📂 Mount</button>
      <button id="umount">📤 Unmount</button>
      <ul id="file-list"></ul>
    
      <script src="renderer.js"></script>
    </body>
    </html>
    

render.js

  1. 下記のように修正します
    const { ipcRenderer } = require('electron');
    
    document.getElementById('mount').onclick = async () => {
      try {
        const files = await ipcRenderer.invoke('mount-nfs');
        const ul = document.getElementById('file-list');
        ul.innerHTML = ''; // 初期化
        files.forEach(f => {
          const li = document.createElement('li');
          li.textContent = f;
          ul.appendChild(li);
        });
      } catch (err) {
        alert('マウントに失敗: ' + err);
      }
    };
    
    document.getElementById('umount').onclick = async () => {
      try {
        const result = await ipcRenderer.invoke('umount-nfs');
        alert(result);
        document.getElementById('file-list').innerHTML = '';
      } catch (err) {
        alert('アンマウントに失敗: ' + err);
      }
    };
    

起動

  1. npm startで起動します
    image.png

  2. Mountをクリックします。ファイル名が見えました
    image.png

  3. Unmountをクリックします
    image.png

  4. 消えました
    image.png

ファイルを追加

  1. ファイルを追加します
    $ sudo touch /mnt/share/hello2.txt
    [sudo] password for XXXX: 
    $ ls -l /mnt/share/
    total 4
    -rw-r--r-- 1 root root  0 Apr 20 12:40 hello2.txt
    -rw-r--r-- 1 root root 10 Apr 20 10:46 hello.txt
    $ 
    
  2. Mountをクリックすると、追加し他ファイルが見えました
    image.png

考察

今回、ElectronでNFSマウントを行うUIを作成してみました。次回以降はコピーや削除なども試してみます。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?