[libcamera-devel,RFC,v2,4/7] libcamera: proxy: add default linux proxy

Message ID 20190703080007.21376-5-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • Add IPA process isolation
Related show

Commit Message

Paul Elder July 3, 2019, 8 a.m. UTC
Add a default linux proxy.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
New in v2
- replaces the dummy/linux default shim

 src/libcamera/meson.build                     |  7 +-
 src/libcamera/proxy/meson.build               |  3 +
 src/libcamera/proxy/proxy_linux_default.cpp   | 90 +++++++++++++++++++
 src/libcamera/proxy_worker/meson.build        | 18 ++++
 .../proxy_linux_default_worker.cpp            | 46 ++++++++++
 5 files changed, 160 insertions(+), 4 deletions(-)
 create mode 100644 src/libcamera/proxy/meson.build
 create mode 100644 src/libcamera/proxy/proxy_linux_default.cpp
 create mode 100644 src/libcamera/proxy_worker/meson.build
 create mode 100644 src/libcamera/proxy_worker/proxy_linux_default_worker.cpp

Comments

Laurent Pinchart July 3, 2019, 10:08 p.m. UTC | #1
Hi Paul,

Thank you for the patch.

On Wed, Jul 03, 2019 at 05:00:04PM +0900, Paul Elder wrote:
> Add a default linux proxy.

It's a proxy skeleton only, you should mention that in the commit
message, and explicitly state that the proxy protocol itself is missing.

> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
> New in v2
> - replaces the dummy/linux default shim
> 
>  src/libcamera/meson.build                     |  7 +-
>  src/libcamera/proxy/meson.build               |  3 +
>  src/libcamera/proxy/proxy_linux_default.cpp   | 90 +++++++++++++++++++
>  src/libcamera/proxy_worker/meson.build        | 18 ++++
>  .../proxy_linux_default_worker.cpp            | 46 ++++++++++
>  5 files changed, 160 insertions(+), 4 deletions(-)
>  create mode 100644 src/libcamera/proxy/meson.build
>  create mode 100644 src/libcamera/proxy/proxy_linux_default.cpp
>  create mode 100644 src/libcamera/proxy_worker/meson.build
>  create mode 100644 src/libcamera/proxy_worker/proxy_linux_default_worker.cpp
> 
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 412564f..e470fe7 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -65,10 +65,7 @@ includes = [
>  ]
>  
>  subdir('pipeline')
> -
> -proxy_install_dir = join_paths(get_option('libdir'), 'libcamera', 'proxy')

I forgot to mention in my review of 3/7, should this be 'libexecdir' ?

http://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html

> -config_h.set('IPA_PROXY_DIR',
> -             '"' + join_paths(get_option('prefix'), proxy_install_dir) + '"')
> +subdir('proxy')
>  
>  libudev = dependency('libudev', required : false)
>  
> @@ -103,3 +100,5 @@ libcamera = shared_library('camera',
>  libcamera_dep = declare_dependency(sources : [libcamera_api, libcamera_h],
>                                     include_directories : libcamera_includes,
>                                     link_with : libcamera)
> +
> +subdir('proxy_worker')
> diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build
> new file mode 100644
> index 0000000..508ed5f
> --- /dev/null
> +++ b/src/libcamera/proxy/meson.build
> @@ -0,0 +1,3 @@
> +libcamera_sources += files([
> +    'proxy_linux_default.cpp',
> +])
> diff --git a/src/libcamera/proxy/proxy_linux_default.cpp b/src/libcamera/proxy/proxy_linux_default.cpp
> new file mode 100644
> index 0000000..1de028a
> --- /dev/null
> +++ b/src/libcamera/proxy/proxy_linux_default.cpp
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * proxy_linux_default.cpp - Default Image Processing Algorithm proxy for Linux
> + */
> +
> +#include <iostream>
> +#include <memory>

What do you need memory for ?

> +#include <vector>
> +
> +#include <sched.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <sys/socket.h>
> +#include <sys/types.h>
> +#include <unistd.h>

You can mix the C and C++ headers. And none of the C headers seem to be
needed anymore.

> +
> +#include <libcamera/ipa/ipa_interface.h>
> +#include <libcamera/ipa/ipa_module_info.h>
> +
> +#include "ipa_module.h"
> +#include "ipa_proxy.h"
> +#include "ipc_unixsocket.h"
> +#include "log.h"
> +#include "process.h"
> +#include "process_manager.h"

ProcessManager should really be an internal class.

> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(ProxyLinuxDefault)

Should we define an IPAProxy log category used by all proxies ? I don't
think we really need one category per proxy.

> +
> +class ProxyLinuxDefault : public Proxy

I would name this class IPAProxyLinux to keep it short.

> +{
> +public:
> +	ProxyLinuxDefault(IPAModule *ipam);
> +	~ProxyLinuxDefault();
> +
> +	int init();
> +
> +private:
> +	Process *proc_;
> +
> +	IPCUnixSocket *socket_;
> +};
> +
> +int ProxyLinuxDefault::init()
> +{
> +	std::cout << "initializing IPA via dummy proxy!" << std::endl;

std::cout, really ? :-) (and you can remove iostream above)

> +
> +	return 0;
> +}
> +
> +ProxyLinuxDefault::ProxyLinuxDefault(IPAModule *ipam)
> +	: Proxy(ipam)
> +{
> +	std::cout << "initializing dummy proxy: loading IPA from "
> +		  << ipam->path() << std::endl;
> +
> +	std::vector<int> fds;
> +	std::vector<std::string> args;
> +	args.push_back(ipam->path());
> +	const std::string path = resolvePath("proxy_linux_default");
> +	if (path.empty())
> +		return;
> +
> +	socket_ = new IPCUnixSocket();
> +	int fd = socket_->create();
> +	if (fd < 0)
> +		return;

You can already connect the readyRead signal of the socket to a member
function of this class.

> +	args.push_back(std::to_string(fd));
> +
> +	proc_ = new Process();
> +	proc_->exec(path, args, fds);

The fds vector is empty...

> +
> +	valid_ = true;
> +	return;

No need for an explicit return statement.

That's a lot of work for a constructor that can't return a value. Would
it make sense to add a start() virtual method to the base Proxy class in
order to allow the caller to differentiate between the different error
causes ?

By the way I think the method specific to proxies should have a proxy
prefix in the base Proxy class, as otherwise they could class with the
IPAInterface methods. The start() method would then be called
proxyStart(). What do you think ?

> +}
> +
> +ProxyLinuxDefault::~ProxyLinuxDefault()
> +{
> +	if (proc_)

No need for this check, delete nullptr is a no-op.

> +		delete proc_;

Sounds like you need to do some cleanup in the Process destructor :-) It
would make sense to at least send the SIGKILL signal to the child there
(which I would expose through a public kill() method in the Process
class). Ideally it should also wait for the process to finish but that's
a bit more work that could be handled later. It should be fine if you
record that as a \todo in the Process destructor.

> +	if (socket_)
> +		delete socket_;
> +}
> +
> +REGISTER_IPA_PROXY(ProxyLinuxDefault)
> +
> +}; /* namespace libcamera */
> diff --git a/src/libcamera/proxy_worker/meson.build b/src/libcamera/proxy_worker/meson.build
> new file mode 100644
> index 0000000..6f927c5
> --- /dev/null
> +++ b/src/libcamera/proxy_worker/meson.build
> @@ -0,0 +1,18 @@
> +ipa_proxy_sources = [
> +    ['proxy_linux_default', 'proxy_linux_default_worker.cpp']

ipa_proxy_linux and ipa_proxy_linux_worker.cpp ?

> +]
> +
> +proxy_install_dir = join_paths(get_option('libdir'), 'libcamera', 'proxy')
> +
> +foreach t : ipa_proxy_sources
> +    proxy = executable(t[0],
> +                          t[1],

You could put t[0] and t[1] on the same line.

Weird indentation.

> +                          name_prefix : '',

Isn't name_prefix empty by default for executable ?

> +                          include_directories : includes,

You can set this to libcamera_internal_includes as libcamera_dep implies
libcamera_includes already (see below). If we were to make use of
includes outside of src/libcamera/meson.build then I would rename it to
libcamera_all_includes.

> +                          install : true,
> +                          install_dir : proxy_install_dir,
> +                          link_with : libcamera)

Could you use dependencies : libcamera_dep instead of link_with ?

> +endforeach
> +
> +config_h.set('IPA_PROXY_DIR',
> +             '"' + join_paths(get_option('prefix'), proxy_install_dir) + '"')

Should we merge the proxy_worker directory with the proxy directory ?
There will likely be header files shared by the proxies and their
workers for the definitions of the proxy protocols.

> diff --git a/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp b/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp
> new file mode 100644
> index 0000000..86b7689
> --- /dev/null
> +++ b/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp
> @@ -0,0 +1,46 @@

Missing SPDX + file header.

> +#include <iostream>
> +

No need for a blank line.

> +#include <unistd.h>
> +
> +#include <libcamera/ipa/ipa_interface.h>
> +
> +#include "ipa_module.h"
> +#include "ipc_unixsocket.h"
> +#include "utils.h"
> +
> +using namespace libcamera;
> +
> +int main(int argc, char **argv)
> +{
> +	if (argc < 3) {
> +		std::cout << "hello world! no args" << std::endl;

We need a log strategy for IPA workers... In the meantime could you use
LOG() and make sure to unset LIBCAMERA_LOG_FILE before exec'ing the
process in the Process class ? And for debugging purpose it would likely
be useful to set LIBCAMERA_LOG_FILE manually here to
/tmp/libcamera.worker.$pid.log or something similar.

> +		return 0;

		return EXIT_FAILURE;

> +	}
> +
> +	int fd = std::stoi(argv[2]);
> +	std::cout << "hello world! we're starting! fd = " << fd << ", path = " << argv[1] << std::endl;

Let's already use proper message.

	LOG(IPAProxyWorker, Info)
		<< "Starting worker for IPA module " << argv[1]
		<< " with IPC fd " << fd;

> +
> +	std::unique_ptr<IPAModule> ipam = utils::make_unique<IPAModule>(argv[1]);
> +	if (!ipam->isValid() || !ipam->load()) {
> +		std::cerr << "Houston, we have a problem: IPAModule should be valid but isn't" << std::endl;

		LOG(IPAProxyWorker, Error)
			<< "IPA module " << argv[1] << " is not valid";

> +		return -1;

		return EXIT_FAILURE;

(and below too).

> +	}
> +
> +	IPCUnixSocket socket;
> +	if (socket.bind(fd) < 0) {
> +		std::cerr << "IPC socket binding failed" << std::endl;
> +		return -1;
> +	}

You can already connect the socket readyRead signal to a local slot
where you can receive() the payload and print a message.

> +
> +	std::unique_ptr<IPAInterface> ipa = ipam->createInstance();
> +	if (!ipa) {
> +		std::cerr << "Failed to create IPA interface" << std::endl;
> +		return -1;
> +	}
> +
> +	std::cout << "hello world from the proxy worker!" << std::endl;
> +
> +	/* TODO IPC listening loop */

I'll likely add an event loop class soon. In the meantime, you can use

	EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher();
	while (1)
		dispatcher->processEvents();

> +
> +	return 0;
> +}

Patch

diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 412564f..e470fe7 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -65,10 +65,7 @@  includes = [
 ]
 
 subdir('pipeline')
-
-proxy_install_dir = join_paths(get_option('libdir'), 'libcamera', 'proxy')
-config_h.set('IPA_PROXY_DIR',
-             '"' + join_paths(get_option('prefix'), proxy_install_dir) + '"')
+subdir('proxy')
 
 libudev = dependency('libudev', required : false)
 
@@ -103,3 +100,5 @@  libcamera = shared_library('camera',
 libcamera_dep = declare_dependency(sources : [libcamera_api, libcamera_h],
                                    include_directories : libcamera_includes,
                                    link_with : libcamera)
+
+subdir('proxy_worker')
diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build
new file mode 100644
index 0000000..508ed5f
--- /dev/null
+++ b/src/libcamera/proxy/meson.build
@@ -0,0 +1,3 @@ 
+libcamera_sources += files([
+    'proxy_linux_default.cpp',
+])
diff --git a/src/libcamera/proxy/proxy_linux_default.cpp b/src/libcamera/proxy/proxy_linux_default.cpp
new file mode 100644
index 0000000..1de028a
--- /dev/null
+++ b/src/libcamera/proxy/proxy_linux_default.cpp
@@ -0,0 +1,90 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * proxy_linux_default.cpp - Default Image Processing Algorithm proxy for Linux
+ */
+
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include <sched.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/ipa_module_info.h>
+
+#include "ipa_module.h"
+#include "ipa_proxy.h"
+#include "ipc_unixsocket.h"
+#include "log.h"
+#include "process.h"
+#include "process_manager.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(ProxyLinuxDefault)
+
+class ProxyLinuxDefault : public Proxy
+{
+public:
+	ProxyLinuxDefault(IPAModule *ipam);
+	~ProxyLinuxDefault();
+
+	int init();
+
+private:
+	Process *proc_;
+
+	IPCUnixSocket *socket_;
+};
+
+int ProxyLinuxDefault::init()
+{
+	std::cout << "initializing IPA via dummy proxy!" << std::endl;
+
+	return 0;
+}
+
+ProxyLinuxDefault::ProxyLinuxDefault(IPAModule *ipam)
+	: Proxy(ipam)
+{
+	std::cout << "initializing dummy proxy: loading IPA from "
+		  << ipam->path() << std::endl;
+
+	std::vector<int> fds;
+	std::vector<std::string> args;
+	args.push_back(ipam->path());
+	const std::string path = resolvePath("proxy_linux_default");
+	if (path.empty())
+		return;
+
+	socket_ = new IPCUnixSocket();
+	int fd = socket_->create();
+	if (fd < 0)
+		return;
+	args.push_back(std::to_string(fd));
+
+	proc_ = new Process();
+	proc_->exec(path, args, fds);
+
+	valid_ = true;
+	return;
+}
+
+ProxyLinuxDefault::~ProxyLinuxDefault()
+{
+	if (proc_)
+		delete proc_;
+	if (socket_)
+		delete socket_;
+}
+
+REGISTER_IPA_PROXY(ProxyLinuxDefault)
+
+}; /* namespace libcamera */
diff --git a/src/libcamera/proxy_worker/meson.build b/src/libcamera/proxy_worker/meson.build
new file mode 100644
index 0000000..6f927c5
--- /dev/null
+++ b/src/libcamera/proxy_worker/meson.build
@@ -0,0 +1,18 @@ 
+ipa_proxy_sources = [
+    ['proxy_linux_default', 'proxy_linux_default_worker.cpp']
+]
+
+proxy_install_dir = join_paths(get_option('libdir'), 'libcamera', 'proxy')
+
+foreach t : ipa_proxy_sources
+    proxy = executable(t[0],
+                          t[1],
+                          name_prefix : '',
+                          include_directories : includes,
+                          install : true,
+                          install_dir : proxy_install_dir,
+                          link_with : libcamera)
+endforeach
+
+config_h.set('IPA_PROXY_DIR',
+             '"' + join_paths(get_option('prefix'), proxy_install_dir) + '"')
diff --git a/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp b/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp
new file mode 100644
index 0000000..86b7689
--- /dev/null
+++ b/src/libcamera/proxy_worker/proxy_linux_default_worker.cpp
@@ -0,0 +1,46 @@ 
+#include <iostream>
+
+#include <unistd.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+
+#include "ipa_module.h"
+#include "ipc_unixsocket.h"
+#include "utils.h"
+
+using namespace libcamera;
+
+int main(int argc, char **argv)
+{
+	if (argc < 3) {
+		std::cout << "hello world! no args" << std::endl;
+		return 0;
+	}
+
+	int fd = std::stoi(argv[2]);
+	std::cout << "hello world! we're starting! fd = " << fd << ", path = " << argv[1] << std::endl;
+
+	std::unique_ptr<IPAModule> ipam = utils::make_unique<IPAModule>(argv[1]);
+	if (!ipam->isValid() || !ipam->load()) {
+		std::cerr << "Houston, we have a problem: IPAModule should be valid but isn't" << std::endl;
+		return -1;
+	}
+
+	IPCUnixSocket socket;
+	if (socket.bind(fd) < 0) {
+		std::cerr << "IPC socket binding failed" << std::endl;
+		return -1;
+	}
+
+	std::unique_ptr<IPAInterface> ipa = ipam->createInstance();
+	if (!ipa) {
+		std::cerr << "Failed to create IPA interface" << std::endl;
+		return -1;
+	}
+
+	std::cout << "hello world from the proxy worker!" << std::endl;
+
+	/* TODO IPC listening loop */
+
+	return 0;
+}