21#include <fmt/format.h>
23#include <wx/datetime.h>
25#include <wx/stdpaths.h>
38#include <api/common/envelope.pb.h>
44using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiStatusCode;
61 if( !
Pgm().GetCommonSettings()->m_Api.enable_server )
63 wxLogTrace( traceApi,
"Server: disabled by user preferences." );
82 socket.AssignDir( wxS(
"/tmp" ) );
84 socket.AssignDir( wxStandardPaths::Get().GetTempDir() );
87 socket.AppendDir( wxS(
"kicad" ) );
88 socket.SetFullName( wxS(
"api.sock" ) );
115 if( !socket.IsAbsolute() )
116 socket.MakeAbsolute();
121 wxLogTrace(
traceApi, wxString::Format(
"Server: socket path %s could not be created",
122 socket.GetPath() ) );
131 wxFileName lockFilePath( socket.GetPath(), wxS(
"api.lock" ) );
133 int lockFile = open( lockFilePath.GetFullPath().c_str(), O_RDONLY | O_CREAT, 0600 );
135 if( lockFile >= 0 && flock( lockFile, LOCK_EX | LOCK_NB ) == 0 )
137 if( socket.Exists() )
139 wxLogTrace(
traceApi, wxString::Format(
"Server: cleaning up stale socket path %s",
140 socket.GetFullPath() ) );
141 wxRemoveFile( socket.GetFullPath() );
146 if( socket.Exists() )
148 socket.SetFullName( wxString::Format( wxS(
"api-%lu.sock" ), ::wxGetProcessId() ) );
150 if( socket.Exists() )
152 wxLogTrace(
traceApi, wxString::Format(
"Server: PID socket path %s already exists!",
153 socket.GetFullPath() ) );
158 m_server = std::make_unique<KINNG_REQUEST_SERVER>(
159 fmt::format(
"ipc://{}", socket.GetFullPath().ToStdString() ) );
164 wxLogTrace(
traceApi,
"Server: failed to start KINNG listener thread" );
175 log( fmt::format(
"--- KiCad API server started at {} ---\n",
SocketPath() ) );
188 wxLogTrace(
traceApi,
"Stopping server" );
204 wxCHECK( aHandler, );
223 handler->onNetSettingsChanged();
228 const wxString& aOldPath,
const wxString& aNewPath )
245 ApiResponse notHandled;
246 notHandled.mutable_status()->set_status( ApiStatusCode::AS_NOT_READY );
247 notHandled.mutable_status()->set_error_message(
"KiCad is not ready to reply" );
248 m_server->Reply( notHandled.SerializeAsString() );
249 log(
"Got incoming request but was not yet ready to reply." );
253 wxCommandEvent* evt =
new wxCommandEvent( API_REQUEST_EVENT );
256 evt->SetClientData(
static_cast<void*
>( aRequest ) );
265 std::string& requestString = *
static_cast<std::string*
>( aEvent.GetClientData() );
274 if( !request.ParseFromString( aRequestString ) )
277 error.mutable_header()->set_kicad_token(
m_token );
278 error.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
279 error.mutable_status()->set_error_message(
"request could not be parsed" );
280 m_server->Reply( error.SerializeAsString() );
283 log(
"Response (ERROR): " + error.Utf8DebugString() );
289 log(
"Request: " + request.Utf8DebugString() );
291 if( !request.header().kicad_token().empty() &&
292 request.header().kicad_token().compare(
m_token ) != 0 )
295 error.mutable_header()->set_kicad_token(
m_token );
296 error.mutable_status()->set_status( ApiStatusCode::AS_TOKEN_MISMATCH );
297 error.mutable_status()->set_error_message(
298 "the provided kicad_token did not match this KiCad instance's token" );
299 m_server->Reply( error.SerializeAsString() );
302 log(
"Response (ERROR): " + error.Utf8DebugString() );
308 bool notifyNetSettings =
false;
312 result = handler->Handle( request );
313 notifyNetSettings |= handler->clearNetSettingsNotification();
317 else if(
result.error().status() != ApiStatusCode::AS_UNHANDLED )
321 if( notifyNetSettings )
332 log(
"Response: " +
result->Utf8DebugString() );
337 error.mutable_status()->CopyFrom(
result.error() );
338 error.mutable_header()->set_kicad_token(
m_token );
340 if(
result.error().status() == ApiStatusCode::AS_UNHANDLED )
342 std::string type =
"<unparseable Any>";
343 google::protobuf::Any::ParseAnyTypeUrl( request.message().type_url(), &type );
344 std::string msg = fmt::format(
"no handler available for request of type {}", type );
345 error.mutable_status()->set_error_message( msg );
348 m_server->Reply( error.SerializeAsString() );
351 log(
"Response (ERROR): " + error.Utf8DebugString() );
358 FILE* fp = wxFopen(
m_logFilePath.GetFullPath(), wxT(
"a" ) );
364 wxDateTime now = wxDateTime::Now();
366 fprintf( fp,
"%s",
TO_UTF8( out.Format( wxS(
"%s: %s" ),
367 now.FormatISOCombined(), aOutput ) ) );
tl::expected< ApiResponse, ApiResponseStatus > API_RESULT
wxDEFINE_EVENT(API_REQUEST_EVENT, wxCommandEvent)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void setApiServer(KICAD_API_SERVER *aServer)
Set by KICAD_API_SERVER::RegisterHandler; null when the handler is not registered.
void handleApiEvent(wxCommandEvent &aEvent)
Event handler that receives the event on the main thread sent by onApiRequest.
std::function< void(kiapi::common::types::DocumentType, const wxString &, const wxString &)> m_renameListener
static wxString s_logFileName
void RegisterHandler(API_HANDLER *aHandler)
Adds a new request handler to the server.
void onApiRequest(std::string *aRequest)
Callback that executes on the server thread and generates an event that will be handled by the wxWidg...
wxString m_socketPathOverride
std::set< API_HANDLER * > m_handlers
void NotifyDocumentRenamed(kiapi::common::types::DocumentType aType, const wxString &aOldPath, const wxString &aNewPath)
Notifies the host application that a document was saved to a new path.
std::atomic< bool > m_readyToReply
std::string SocketPath() const
void handleApiRequestString(std::string &aRequestString)
static std::string StandardSocketUrl()
Return the default API socket URL (including the ipc:// scheme).
void log(const std::string &aOutput)
KICAD_API_SERVER(bool aAutoStart=true)
void DeregisterHandler(API_HANDLER *aHandler)
static wxFileName StandardSocketPath()
Return the default API socket path (without the ipc:// scheme).
void NotifyNetSettingsChanged()
Calls onNetSettingsChanged() on every registered handler, so that editors can update state derived fr...
std::unique_ptr< KINNG_REQUEST_SERVER > m_server
static wxString GetLogsPath()
Gets a path to use for user-visible log files.
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
PGM_BASE & Pgm()
The global program "get" accessor.
wxString result
Test unit parsing edge cases and error handling.