#SNAPパッケージとは
アプリケーション・ソフトウェアとその依存パッケージをバンドルしたパッケージになります。SNAPパッケージはSnapStoreを通して公開されており、様々な既存アプリケーションのスナップパッケージが登録されています。
#環境
Ubuntu 20.04がインストールされたラップトップでテストをしています。デフォルトでインストールされていない場合は、リファレンスにSNAPをサポートするDistroと各Distroでのスナップの使い方が書いてあります。またサンプルプログラムはこちらのページを参考にさせて頂きました。
#SNAPパッケージ作成の流れ
SNAPパッケージを作成するには大きく2つのやり方があります。
- Dumpプラグインを利用して、バイナリファイルを直接パッケージ化する
- 他のプラグインを利用して、ソースファイルをビルドしてパッケージ化する
今回はcmakeプラグインを利用してソースコードをビルドし、シンプルなサーバー、クライアンとプログラムをSNAP化してみようと思います。
#ClientとServerプログラムを作る
ローカルホストに接続して、データを送るクライアントプログラムを作成します。
Serverコード
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "sample_data_type.h"
int main()
{
unsigned short port = PORT_NUM;
int server_soket = 0;
int client_soket = 0;
struct sockaddr_in server_address = {0};
struct sockaddr_in client_address = {0};
unsigned int client_addressSize = sizeof(client_address);
int ret_val = 0;
//Configure sockaddr_in
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
//create a socket
errno = 0;
server_soket = socket(AF_INET, SOCK_STREAM, 0);
if(server_soket == -1){
printf("socket() failed. errno : %d\n", errno);
return -1;
}
// bind socket
errno = 0;
ret_val = bind(server_soket, (struct sockaddr*) &server_address, sizeof(server_address));
if(ret_val == -1){
printf("bind() failed. errno : %d\n", errno);
return -1;
}
// Connect
errno = 0;
ret_val = listen(server_soket, 1);
if(ret_val == -1){
printf("listen() failed. errno : %d\n", errno);
return -1;
}
//wait for connection
printf("Waiting for connection\n");
int retry_count = 0;
while(retry_count < 3){
errno = 0;
client_soket = accept(server_soket, (struct sockaddr*)&client_address, &client_addressSize);
if(client_soket == -1){
printf("accept() failed. count : %d errno : %d\n", retry_count, errno);
++retry_count;
}else{
break;
}
if(retry_count < 3){
sleep(1);
}
}
if(client_soket == -1){
return -1;
}
printf("Connected from localhost %s\n", inet_ntoa(client_address.sin_addr));
//Receive packet
while(1){
int byte_received = 0;
SampleDataType received_data = {0};
errno = 0;
byte_received = recv(client_soket, &received_data, sizeof(received_data), 0);
if(byte_received == 0){
break;
}
if(byte_received == -1){
printf("recv() failed. errno : %d\n", errno);
break;
}
printf("received: %s\n",
received_data.text);
}
// Close socket
errno = 0;
int status = close(client_soket);
if(status == -1){
printf("Send: close() failed. errno : %d\n", errno);
}
//Close socket for receiving data
errno = 0;
status = close(server_soket);
if(status == -1){
printf("Receive: close() failed. errno : %d\n", errno);
}
return 0;
}
Clientコード
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "sample_data_type.h"
int main()
{
char dest_ip[80] = {0};
unsigned short port = PORT_NUM;
int client_socket = 0;
struct sockaddr_in client_addr = {0};
printf("Connect to local host.");
strncpy(dest_ip, "127.0.0.1", sizeof(dest_ip));
// configure socket_addr
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(port);
client_addr.sin_addr.s_addr = inet_addr(dest_ip);
// Create a socket
errno = 0;
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if(client_socket == -1){
printf("socket() failed. errno : %d\n", errno);
return -1;
}
//Connect to socket
printf("Connect to localhost: \n");
int ret_val = connect(client_socket, (struct sockaddr*) &client_addr, sizeof(client_addr));
if(ret_val == -1){
printf("connect() failed. errno : %d\n", errno);
return -1;
}
// Send a packet
for(int i=0; i<10; i++) {
int sent_byte = 0;
SampleDataType send_data = {0};
char *text = "Send Sample message";
sprintf(send_data.text, "%s", text);
printf("Sending data to localhost\n");
errno = 0;
ret_val = send(client_socket, &send_data, sizeof(send_data), 0);
if(sent_byte == -1){
printf("send() failed. errno : %d\n", errno);
}
sleep(1);
}
// Close socket
int status = close(client_socket);
if(status == -1){
printf("close() failed. errno : %d\n", errno);
return -1;
}
return 0;
}
sample_data_type.h
#ifndef SAMPLE_DATA_TYPE_H_
#define SAMPLE_DATA_TYPE_H_
// データ
typedef struct{
char text[256];
} SampleDataType;
// ポート番号
#define PORT_NUM 9000
#endif // SAMPLE_DATA_TYPE_H_
#Snapcraftを使用してSNAPパッケージを作成する
前のステップで作成したサーバープログラムとクライアントプログラムをそれぞれ
別々のパッケージとして作成してみます。
SNAPパッケージの作成にはsnapcraftというSNAPを使用しするため、まずはsnapcraft
SNAPを以下のコマンドでインストールします。またスナップパッケージを作成する際に
デフォルトでMultipass(VM)を使用するのでこちらもインストールします。
snap install snapcraft --classic
sudo snap install multipass
####snapcraft.yamlを作成する
snapcraftはsnapcrafy.yamlに記載された情報をもとにパッケージを作成するので、
snapcrafy.yamlを作成、編集していきます。
snapcraft init
Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.
snapcraft initを実行したパスの直下にsnapというフォルダにsnapcraft.yamlが作成されます。
クライアント、サーバーの別々のパッケージを作成するので、それぞれのフォルダでsnapcraft init
を実行しsnapcraft.yamlを作成します。
tree -d
.
├── client
│ ├── snap
│ └── src
└── server
├── snap
└── src
####snapcraft.yamlの編集
作成されたYAMLファイルを編集します。
snapcraft.yamlファイルのフォーマッとのリファレンス
ここでは必要な部分のみを変更しました。
name:
SNAPパッケージの名前。SnapStoreに登録する場合は、ストア内でSNAPパッケージを一意に設定する必要があります。同じ名前のパッケージは複数存在できません。例えばhello-worldというパッケージはすでにストアに
存在するので、hello-worldというSNAPパッケージを作ることはできません。
base:
SNAPパッケージに実行環境を提供するベースsnap。
version:
作成するSANPパッケージのバージョン
summary:
ストアに表示されるSNAPパッケージのサマリ
description: |
SNAPパッケージの詳細
parts:
SNAPパッケージを構成するパーツ。このサブフィールドで、ビルドの仕方やソースファイルの場所、ビルドに必要なパッケージなどを指定します。ここではそれぞれのプログラムのソースファイルの位置を指定します。
apps:
SNAPパッケージに含まれる実行ファイルを指定します。サブフィールドで、SANPパッケージに含まれるアプリ、必要なインターフェースを指定します。ここではビルドされたバイナリファイルを指定し、ソケット通信を行うのに必要なインターフェース(network-bind)を指定しています。
Client:snapcraft.yaml
name: snapclient # you probably want to 'snapcraft register <name>'
base: core18 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Demo snap for snap communication
description: |
snap client. Sends pre-defined test message to snap server.
grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots
parts:
snapclient:
# See 'snapcraft plugins'
plugin: cmake
source: src
build-packages:
- g++
- make
apps:
snapclient:
command: tcp_client
plugs:
- network
Server:snapcraft.yaml
name: snapserver # you probably want to 'snapcraft register <name>'
base: core18 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Demo snap for snap communication
description: |
This is my-snap's description. You have a paragraph or two to tell the
most important story about your snap. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the snap
store.
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots
parts:
snapserver:
# See 'snapcraft plugins'
plugin: cmake
source: src
build-packages:
- g++
- make
apps:
snapserver:
command: tcp_server
plugs:
- network-bind
####パッケージ化を実行する
パッケージを作成するにはClientあるいはServerフォルダ内で、snapcraftコマンドを実行します。
Stealth-9SE:~/study/simple_client_server/server$ snapcraft
Launching a VM.
Running with 'sudo' may cause permission errors and is discouraged. Use 'sudo' when cleaning.
snapd is not logged in, snap install commands will use sudo
snap "core18" has no updates available
Skipping pull snapserver (already ran)
Skipping build snapserver (already ran)
Skipping stage snapserver (already ran)
Skipping prime snapserver (already ran)
The command 'tcp_server' for 'tcp_server' was resolved to 'bin/tcp_server'.
The command 'tcp_server' has been changed to 'bin/tcp_server'.
The requested action has already been taken. Consider
specifying parts, or clean the steps you want to run again.
Snapping |
Snapped snapserver_0.1_amd64.snap
masahiro@masahiro-GS65-Stealth-9SE:~/study/simple_client_server/server$ snapcraft clean
masahiro@masahiro-GS65-Stealth-9SE:~/study/simple_client_server/server$ snapcraft
Launching a VM.
Launched: snapcraft-snapserver
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:3 http://security.ubuntu.com/ubuntu bionic-security/multiverse Sources [3192 B]
Get:4 http://security.ubuntu.com/ubuntu bionic-security/restricted Sources [7828 B]
Get:5 http://security.ubuntu.com/ubuntu bionic-security/universe Sources [173 kB]
Get:6 http://security.ubuntu.com/ubuntu bionic-security/main Sources [166 kB]
Get:7 http://security.ubuntu.com/ubuntu bionic-security/main amd64 Packages [849 kB]
Get:8 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
(略)
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/parts/snapserver/build
cmake --build . -- -j2
Scanning dependencies of target tcp_server
[ 50%] Building C object CMakeFiles/tcp_server.dir/tcp_server.c.o
[100%] Linking C executable tcp_server
[100%] Built target tcp_server
cmake --build . --target install
[100%] Built target tcp_server
Install the project...
-- Install configuration: ""
-- Installing: /root/parts/snapserver/install/bin/tcp_server
Staging snapserver
Priming snapserver
The command 'tcp_server' for 'tcp_server' was resolved to 'bin/tcp_server'.
The command 'tcp_server' has been changed to 'bin/tcp_server'.
Snapping |
Snapped snapserver_0.1_amd64.snap
これで SNAPパッケージ(snapserver_0.1_amd64.snap)が作成されました。
ClinetSNAPパッケージも同様に作成できます。
#作成したSNAPパッケージをインストールして実行する
ローカル上で作成されたSNAPパッケージをインストールするには"--dangerous"オプションが必要になります。
:~/study/simple_client_server/server$ snap install snapserver_0.1_amd64.snap --dangerous
snapserver 0.1 installed
同様にクライアントSNAPパッケージもインストールして実行してみると、異なるSNAPパッケージの間
でデータのやり取りが出来ていることが確認できました。
:~/study/simple_client_server/server$ snapserver
Waiting for connection
Connected from localhost 127.0.0.1
received: Send Sample message
received: Send Sample message
received: Send Sample message
received: Send Sample message
received: Send Sample message
received: Send Sample message
:~/study/simple_client_server/client$ snapclient
Connect to local host.Connect to localhost:
Sending data to localhost
Sending data to localhost
Sending data to localhost
Sending data to localhost
Sending data to localhost
Sending data to localhost
Sending data to localhost
中川 雅裕 Canonical Japan (Iot Field Engineer)
Ubuntu Blog: https://jp.ubuntu.com/blog
Canonical Japan: https://jp.ubuntu.com/
Email: masahiro.nakagawa@canonical.com
Email:info-jp@canonical.com
Phone: 03-6205-3075