# LNXALL网关自动升级功能说明

### 平台给网关发送固件升级操作

当网关连上云平台后，云平台应该只网关发送一个固件升级的消息，而且只发一次。该消息的`topic`和`payload`为：

```
M/[GW SN]/gateway/device/[GW SN]/service/Firmware_Upgrade
{
        "mi":1,
        "md5":"",
        "firmware_url":""
}
```

其中，`md5`为固件升级包的MD5较验值，该值必须存在，用于网关较验下载升级包的完整性。`firmware_url`为固件升级包的下载路径，不能包含双引号、单引号和反斜杠`"`、`'`、`\`这三个字符。

### `config_manager`对升级消息的处理

`config_manager`在收到该消息后，会判断`/app/config/firmware_upgrade.json`文件是否存在，如果存在而且其中保存的`md5`值与该消息中的`md5`值相同，而且`/app/config/firmware_upgrade.json`中的`upgrade`为字符串`"1"`，则说明升级已经完成，`config_manager`会忽略该消息。

如果`/app/config/firmware_upgrade.json`文件不存在，或者`md5`值不同，或者`upgrade`不为字符串`"1"`，`config_manager`会重写`/app/config/firmware_upgrade.json`，并执行：

```sh
/etc/init.d/firmware_upgrade reload
```

以启动固件升级服务。升级服务由lua脚本编写，可以通过执行`lua /app/firmware_upgrade.lua`手动运行之。

### 固件升级服务的操作流程

升级服务不会一直运行，其启动脚本的`raspawn`参数使得`procd`不会反复重启该服务。该服务启动时，会读取`/app/config/firmware_upgrade.json`文件。若该文件不存在，其会等待60秒后退出。

当`firmware_upgrade.json`文件中的`upgrade`字段为`"1"`时，说明升级已操作过，不会进行固件的下载、升级。否则它会依次调用`curl`或者`wget`以下载固件，若固件下载成功且`md5`值较验是匹配的，则会重写`firmware_upgrade.json`文件，将`upgrade`字段置为字符串`"1"`，并调用`sysupgrade`以执行网关的固件升级：

```sh
/sbin/sysupgrade /path/to/firmware </dev/null </dev/null 2>&1 &
```

### 固件升级的注意事项

1. 固件包下载路径为`/tmp`目录。`mips`平台的网关Flash存储空间不足，只能下载到`/tmp`目录下。但当网关的内存不足时，下载固件可能会因`/tmp`空间不足不能完成下载操作。不过在一台`mips`网关上测试升级成功，未发现该问题。
2. 升级服务随系统自启动。当其在下载固件时，`config_manager`执行`/etc/init.d/firmware_upgrade reload`，会中断当前的固件下载操作，`firmware_upgrade.lua`会杀死`curl`、`wget`等下载进程，并删除下载的固件。不过当一个固件下载完成后，调用`/sbin/sysupgrade`的操作可视为原子的（要么调用了`/sbin/sysupgrade &`，要么没调用），一定程度上保证固件升级的可靠性。
3. 目前未在`payload`中制定取消固件升级的字段。当一个固件升级后，在平台上未绑定新的固件`URL`，网关不会重复升级。但一个固件未完成时，需要取消升级操作，只需在平台端将`md5`值置空（或长度不等于32字节的`md5`），或者配置一个无效的`URL`（这样下载固件会失败从而不会执行升级操作），`config_manger`收到该消息后会取消现有固件升级任务。
3. 网关通过比对平台发过来的`md5`值，或者写入`/app/config/firmware_upgrade.json`中的`upgrade`字段，决定是否进行固件升级；**而不是通过固件的版本号来决定是否升级**。当按照第`3`小节中的操作取消固件升级后，再次配置正确的升级参数，网关可能重复升级相同的固件。