Systemd启动Node.js程序时部分exec()调用失效问题求助
问题排查:systemd服务下Node.js程序部分exec命令失效
问题描述
Ubuntu环境下开发的Node.js程序,直接运行时所有exec()调用的Shell命令均正常工作,但注册为systemd服务(服务名speaker)后,仅/usr/local/bin/gpio相关命令正常,/usr/bin/amixer、/usr/bin/firefox等命令全部失效。
相关配置与代码
systemd服务配置(/etc/systemd/system/speaker.service)
[Service] ExecStart=/home/speaker/speakerModule/start.sh WorkingDirectory=/home/speaker/speakerModule User=speaker [Install] WantedBy=multi-user.target
启动脚本(/home/speaker/speakerModule/start.sh)
#!/bin/bash /home/speaker/.nvm/versions/node/v18.13.0/bin/node speaker.js
Node.js核心代码片段
function setVolume(volume) { if (volume < 0 || volume > 100) return; if (volume === 0) { exec(`/usr/local/bin/gpio -g write ${RELAY} 0`); } else { exec(`/usr/local/bin/gpio -g write ${RELAY} 1`); } exec(`/usr/bin/amixer -D pulse sset Master ${volume}%`); } // ...其他代码 setVolume(30); socket.connect(); exec(`/usr/local/bin/gpio -g mode ${RELAY} out`); exec(`/usr/bin/firefox ${PLAYER_CONTROL_ENDPOINT}`);
问题原因分析
- 启动脚本格式错误:
start.sh的Shebang行写法错误,#!/bin/bash后直接拼接Node命令不符合Shell脚本规范,导致脚本执行逻辑异常。 - systemd服务的环境缺失:
multi-user.target下无桌面会话,firefox需要X11显示环境才能启动;amixer依赖PulseAudio的用户会话,systemd服务默认没有加载该会话的环境变量;- systemd服务的默认环境变量集远小于普通用户会话,部分命令依赖的隐性环境未配置。
- 错误处理缺失:Node.js的
exec()调用未添加错误回调,无法捕获命令执行失败的具体原因,增加排查难度。
解决方案
1. 修复启动脚本格式
修改start.sh为标准Shell脚本格式:
#!/bin/bash /home/speaker/.nvm/versions/node/v18.13.0/bin/node speaker.js
2. 调整systemd服务配置
更新/etc/systemd/system/speaker.service,补充环境变量与会话支持:
[Service] ExecStart=/home/speaker/speakerModule/start.sh WorkingDirectory=/home/speaker/speakerModule User=speaker # 配置X11显示与音频会话环境 Environment=DISPLAY=:0 Environment=XAUTHORITY=/home/speaker/.Xauthority Environment=PULSE_SERVER=/run/user/$(id -u speaker)/pulse/native # 确保服务启动前等待桌面环境就绪 PermissionsStartOnly=true ExecStartPre=/bin/sleep 5 # 自动重启服务 Restart=always [Install] # 改为依赖桌面会话目标 WantedBy=graphical.target
3. 配置用户权限
允许speaker用户访问X11桌面:
xhost +local:speaker
4. 为exec调用添加错误处理
修改Node.js代码中的exec()调用,捕获执行错误便于排查:
// 示例:amixer命令的错误处理 exec(`/usr/bin/amixer -D pulse sset Master ${volume}%`, (error, stdout, stderr) => { if (error) { console.error(`amixer执行失败: ${error.message}`); return; } if (stderr) { console.error(`amixer错误输出: ${stderr}`); return; } }); // 示例:firefox命令的错误处理 exec(`/usr/bin/firefox ${PLAYER_CONTROL_ENDPOINT}`, (error, stdout, stderr) => { if (error) { console.error(`firefox启动失败: ${error.message}`); return; } });
5. 重启服务生效
sudo systemctl daemon-reload sudo systemctl restart speaker.service
内容的提问来源于stack exchange,提问作者DONGHAEY
相关产品推荐
相关产品推荐

