UNPKG

node-fstab

Version:

fstab management with node.js

258 lines (240 loc) 8.42 kB
const fs = require('fs').promises; const { createReadStream, createWriteStream } = require('fs'); const { exec } = require('child_process'); // Helper function to run a shell command with exec wrapped in a Promise const runCommand = (command) => { return new Promise((resolve, reject) => { exec(command, (error, stdout, stderr) => { if (error) { reject({ error, stderr }); } else { resolve({ stdout, stderr }); } }); }); }; // Create the mount point if it doesn't exist const createMountPoint = async (path) => { let response = { ok: false }; try { await fs.mkdir(path, { recursive: true }); response.ok = true; } catch (error) { if (error.code !== 'EEXIST') { response.error = `Failed to create mount point: ${error.message}`; } else { response.ok = true; } } return response; }; // Mount the NFS share using a system command with customizable options const mount = async (server, remotePath, localPath, type = 'cifs', options = 'username=username,password=password') => { let response = { ok: false }; const mountCommand = `sudo mount -t ${type} ${server}:${remotePath} ${localPath} ${options}`.trim(); try { const { stderr } = await runCommand(mountCommand); if (stderr) { response.error = `Error mounting: ${stderr}`; } else { response.ok = true; } } catch (error) { response.error = `Failed to mount : ${error.message}`; } return response; }; // Execute `mount -a` to remount all filesystems from /etc/fstab const remountAll = async () => { let response = { ok: false }; try { const { stdout, stderr } = await runCommand('mount -a'); if (stderr) { response.error = `Error remounting all filesystems: ${stderr}`; } else { response.ok = true; response.message = stdout.trim() || "Successfully remounted all filesystems from /etc/fstab"; } } catch (error) { response.error = `Failed to remount all filesystems: ${error.message}`; } return response; }; const remount = async (mountPath) => { let response = { ok: false }; try { const { stdout, stderr } = await runCommand(`mount ${mountPath}`); const { stdout: daemonStdout, stderr: daemonStderr } = await runCommand(`systemctl daemon-reload`); if (stderr) { response.error = `Error remounting ${mountPath}: ${stderr}`; } else { response.ok = true; response.message = `Successfully remounted ${mountPath}`; } } catch (failedResponse) { // console.error(error) response.error = `Failed to remount ${mountPath}: ${failedResponse.error}`; } return response; }; const unmount = async (localPath) => { let response = { ok: false }; try { const { stdout, stderr } = await runCommand(`sudo umount ${localPath}`); if (stderr) { response.error = `Error unmounting ${localPath}: ${stderr}`; } else { response.ok = true; response.message = `Successfully unmounted ${localPath}`; } } catch (error) { response.error = `Failed to unmount ${localPath}: ${error.message}`; } return response; }; // Check and update /etc/fstab with the NFS entry with customizable options const update = async (sourceTarget, localPath, mountType = 'cifs', fstabOptions = 'username=username,password=password,rw,iocharset=utf8,file_mode=0777,dir_mode=0777 0 0') => { let response = { ok: false }; const fstabEntry = `${sourceTarget} ${localPath} ${mountType} ${fstabOptions}\n`; try { // Read the current /etc/fstab file const data = await fs.readFile('/etc/fstab', 'utf8'); // Remove any existing entries with the same localPath const updatedData = data .split('\n') .filter(line => !line.includes(` ${localPath} `)) .join('\n'); // Write the modified data back to /etc/fstab without duplicates await fs.writeFile('/etc/fstab', updatedData, 'utf8'); // Append the new fstab entry await fs.appendFile('/etc/fstab', fstabEntry); response.ok = true; response.message = `Successfully updated /etc/fstab with entry for ${localPath}`; } catch (error) { response.error = `Error updating /etc/fstab: ${error.message}`; } return response; }; // Remove an entry from /etc/fstab by mount point const remove = async (localPath) => { let response = { ok: false }; try { const data = await fs.readFile('/etc/fstab', 'utf8'); const updatedData = data .split('\n') .filter(line => !line.includes(` ${localPath} `)) .join('\n'); if (updatedData !== data) { await fs.writeFile('/etc/fstab', updatedData, 'utf8'); response.ok = true; } else { response.error = `No entry found for ${localPath} in /etc/fstab.`; } } catch (error) { response.error = `Error removing entry from /etc/fstab: ${error.message}`; } return response; }; // fstab mounts const list = async () => { let response = { ok: false, mounts: [] }; try { const data = await fs.readFile('/etc/fstab', 'utf8'); response.ok = true; response.mounts = data .split('\n') .filter(line => line && !line.startsWith('#')) // Ignore empty lines and comments .map(line => { const [device, mountPoint, type, options, dump, pass] = line.split(/\s+/); return { device, // The device or UUID mountPoint, // The mount point on the local machine type, // Filesystem type options: `${options} ${dump} ${pass}`, // Mount options as an array }; }) .filter(item => !!item.mountPoint && !item.device.includes('/dev/disk') && !item.device.includes('/swap') ); } catch (error) { response.error = `Failed to read /etc/fstab: ${error.message}`; } return response; }; // Get disk usage information using `df -h` as an array of objects const getDiskUsage = async () => { let response = { ok: false, disks: [] }; try { const { stdout, stderr } = await runCommand('df -h'); if (stderr) { response.error = `Error retrieving disk usage: ${stderr}`; } else { const lines = stdout.trim().split('\n'); const headers = lines[0].toLowerCase().split(/\s+/); response.ok = true; response.disks = lines.slice(1).map(line => { const values = line.split(/\s+/); return headers.reduce((obj, header, index) => { obj[header] = values[index]; return obj; }, {}); }); } } catch (error) { response.error = `Failed to retrieve disk usage: ${error.message}`; } return response; }; // Check if a specific local path exists in the disk usage list const checkDiskPathExists = async (localPath) => { let response = { ok: false, exists: false }; try { const diskUsage = await getDiskUsage(); if (!diskUsage.ok) { response.error = `Error retrieving disk usage: ${diskUsage.error}`; } else { response.exists = diskUsage.disks.some(disk => disk.mounted === localPath); response.ok = true; response.message = response.exists ? `Path ${localPath} exists` : `Path ${localPath} does not exist`; } } catch (error) { response.error = `Failed to check if path exists: ${error.message}`; } return response; }; const writeReadStream = async (readerStream, outputFile) => { return new Promise((resolve, reject) => { const response = { ok: false } const writerStream = createWriteStream(outputFile); readerStream.on("error", (error) => { response.err = error.toString(); resolve(response) }); writerStream.on("error", (error) => { response.err = error.toString(); resolve(response) }); writerStream.on("finish", () => { response.ok = true; resolve(response) }); readerStream.pipe(writerStream); }); }; // Export functions module.exports = { createMountPoint, mount, update, remove, list, remountAll, remount, unmount, getDiskUsage, writeReadStream, checkDiskPathExists, execAsync: runCommand };