1 // CODYlib -*- mode:c++ -*-
2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3 // License: Apache v2.0
8 // If the user specifies this as non-zero, it must be what we expect,
9 // generally only good for requesting no networking
10 #if !defined (CODY_NETWORKING)
11 // Have a known-good list of networking systems
12 #if defined (__unix__) || defined (__MACH__)
13 #define CODY_NETWORKING 1
15 #define CODY_NETWORKING 0
18 #undef CODY_NETWORKING
19 #define CODY_NETWORKING 0
31 #include <sys/types.h>
33 #include <sys/socket.h>
38 // Set version to 1, as this is completely incompatible with 0.
39 // Fortunately both versions 0 and 1 will recognize each other's HELLO
40 // messages sufficiently to error out
41 constexpr unsigned Version
= 1;
43 // FIXME: I guess we need a file-handle abstraction here
44 // Is windows DWORDPTR still?, or should it be FILE *? (ew).
48 // C++11 doesn't have utf8 character literals :(
51 constexpr char S2C (char const (&s
)[I
])
53 static_assert (I
== 2, "only single octet strings may be converted");
57 /// Internal buffering class. Used to concatenate outgoing messages
58 /// and Lex incoming ones.
61 std::vector
<char> buffer
; ///< buffer holding the message
62 size_t lastBol
= 0; ///< location of the most recent Beginning Of
63 ///< Line, or position we've readed when writing
66 MessageBuffer () = default;
67 ~MessageBuffer () = default;
68 MessageBuffer (MessageBuffer
&&) = default;
69 MessageBuffer
&operator= (MessageBuffer
&&) = default;
73 /// Finalize a buffer to be written. No more lines can be added to
74 /// the buffer. Use before a sequence of Write calls.
75 void PrepareToWrite ()
77 buffer
.push_back (u8
"\n"[0]);
81 /// Prepare a buffer for reading. Use before a sequence of Read calls.
89 /// Begin a message line. Use before a sequence of Append and
92 /// End a message line. Use after a sequence of Append and related calls.
96 /// Append a string to the current line. No whitespace is prepended
99 /// @param str the string to be written
100 /// @param maybe_quote indicate if there's a possibility the string
101 /// contains characters that need quoting. Defaults to false.
102 /// It is always safe to set
103 /// this true, but that causes an additional scan of the string.
104 /// @param len The length of the string. If not specified, strlen
105 /// is used to find the length.
106 void Append (char const *str
, bool maybe_quote
= false,
107 size_t len
= ~size_t (0));
110 /// Add whitespace word separator. Multiple adjacent whitespace is fine.
113 Append (Detail::S2C(u8
" "));
117 /// Add a word as with Append, but prefixing whitespace to make a
119 void AppendWord (char const *str
, bool maybe_quote
= false,
120 size_t len
= ~size_t (0))
122 if (buffer
.size () != lastBol
)
124 Append (str
, maybe_quote
, len
);
126 /// Add a word as with AppendWord
127 /// @param str the string to append
128 /// @param maybe_quote string might need quoting, as for Append
129 void AppendWord (std::string
const &str
, bool maybe_quote
= false)
131 AppendWord (str
.data (), maybe_quote
, str
.size ());
134 /// Add an integral value, prepending a space.
135 void AppendInteger (unsigned u
);
138 /// Append a literal character.
139 /// @param c character to append
140 void Append (char c
);
143 /// Lex the next input line into a vector of words.
144 /// @param words filled with a vector of lexed strings
145 /// @result 0 if no errors, an errno value on lexxing error such as
146 /// there being no next line (ENOENT), or malformed quoting (EINVAL)
147 int Lex (std::vector
<std::string
> &words
);
150 /// Append the most-recently lexxed line to a string. May be useful
151 /// in error messages. The unparsed line is appended -- before any
153 /// If we had c++17 string_view, we'd simply return a view of the
154 /// line, and leave it to the caller to do any concatenation.
155 /// @param l string to-which the lexxed line is appended.
156 void LexedLine (std::string
&l
);
159 /// Detect if we have reached the end of the input buffer.
160 /// I.e. there are no more lines to Lex
161 /// @result True if at end
162 bool IsAtEnd () const
164 return lastBol
== buffer
.size ();
168 /// Read from end point into a read buffer, as with read(2). This will
169 /// not block , unless FD is blocking, and there is nothing
170 /// immediately available.
171 /// @param fd file descriptor to read from. This may be a regular
172 /// file, pipe or socket.
173 /// @result on error returns errno. If end of file occurs, returns
174 /// -1. At end of message returns 0. If there is more needed
175 /// returns EAGAIN (or possibly EINTR). If the message is
176 /// malformed, returns EINVAL.
177 int Read (int fd
) noexcept
;
180 /// Write to an end point from a write buffer, as with write(2). As
181 /// with Read, this will not usually block.
182 /// @param fd file descriptor to write to. This may be a regular
183 /// file, pipe or socket.
184 /// @result on error returns errno.
185 /// At end of message returns 0. If there is more to write
186 /// returns EAGAIN (or possibly EINTR).
187 int Write (int fd
) noexcept
;
191 /// Request codes. Perhaps this should be exposed? These are likely
192 /// useful to servers that queue requests.
200 RC_INCLUDE_TRANSLATE
,
204 /// Internal file descriptor tuple. It's used as an anonymous union member.
207 int from
; ///< Read from this FD
208 int to
; ///< Write to this FD
213 // Flags for various requests
214 enum class Flags
: unsigned
217 NameOnly
= 1<<0, // Only querying for CMI names, not contents
220 inline Flags
operator& (Flags a
, Flags b
)
222 return Flags (unsigned (a
) & unsigned (b
));
224 inline Flags
operator| (Flags a
, Flags b
)
226 return Flags (unsigned (a
) | unsigned (b
));
230 /// Response data for a request. Returned by Client's request calls,
231 /// which return a single Packet. When the connection is Corked, the
232 /// Uncork call will return a vector of Packets.
237 /// Packet is a variant structure. These are the possible content types.
238 enum Category
{ INTEGER
, STRING
, VECTOR
};
241 // std:variant is a C++17 thing, so we're doing this ourselves.
244 size_t integer
; ///< Integral value
245 std::string string
; ///< String value
246 std::vector
<std::string
> vector
; ///< Vector of string value
248 Category cat
: 2; ///< Discriminator
251 unsigned short code
= 0; ///< Packet type
252 unsigned short request
= 0;
255 Packet (unsigned c
, size_t i
= 0)
256 : integer (i
), cat (INTEGER
), code (c
)
259 Packet (unsigned c
, std::string
&&s
)
260 : string (std::move (s
)), cat (STRING
), code (c
)
263 Packet (unsigned c
, std::string
const &s
)
264 : string (s
), cat (STRING
), code (c
)
267 Packet (unsigned c
, std::vector
<std::string
> &&v
)
268 : vector (std::move (v
)), cat (VECTOR
), code (c
)
271 // No non-move constructor from a vector. You should not be doing
274 // Only move constructor and move assignment
277 Create (std::move (t
));
279 Packet
&operator= (Packet
&&t
)
282 Create (std::move (t
));
293 /// Variant move creation from another packet
294 void Create (Packet
&&t
);
296 /// Variant destruction
301 /// Return the packet type
302 unsigned GetCode () const
307 /// Return the packet type
308 unsigned GetRequest () const
312 void SetRequest (unsigned r
)
317 /// Return the category of the packet's payload
318 Category
GetCategory () const
325 /// Return an integral payload. Undefined if the category is not INTEGER
326 size_t GetInteger () const
331 /// Return (a reference to) a string payload. Undefined if the
332 /// category is not STRING
333 std::string
const &GetString () const
337 std::string
&GetString ()
342 /// Return (a reference to) a constant vector of strings payload.
343 /// Undefined if the category is not VECTOR
344 std::vector
<std::string
> const &GetVector () const
349 /// Return (a reference to) a non-conatant vector of strings payload.
350 /// Undefined if the category is not VECTOR
351 std::vector
<std::string
> &GetVector ()
360 /// Client-side (compiler) object.
364 /// Response packet codes
367 PC_CORKED
, ///< Messages are corked
368 PC_CONNECT
, ///< Packet is integer version
369 PC_ERROR
, ///< Packet is error string
376 Detail::MessageBuffer write
; ///< Outgoing write buffer
377 Detail::MessageBuffer read
; ///< Incoming read buffer
378 std::string corked
; ///< Queued request tags
381 Detail::FD fd
; ///< FDs connecting to server
382 Server
*server
; ///< Directly connected server
384 bool is_direct
= false; ///< Discriminator
385 bool is_connected
= false; /// Connection handshake succesful
390 /// Direct connection constructor.
391 /// @param s Server to directly connect
398 /// Communication connection constructor
399 /// @param from file descriptor to read from
400 /// @param to file descriptor to write to, defaults to from
401 Client (int from
, int to
= -1)
405 fd
.to
= to
< 0 ? from
: to
;
408 // We have to provide our own move variants, because of the variant member.
410 Client
&operator= (Client
&&);
414 /// Direct connection predicate
415 bool IsDirect () const
420 /// Successful handshake predicate
421 bool IsConnected () const
429 /// @result the FD to read from, -1 if a direct connection
430 int GetFDRead () const
432 return is_direct
? -1 : fd
.from
;
436 /// @result the FD to write to, -1 if a direct connection
437 int GetFDWrite () const
439 return is_direct
? -1 : fd
.to
;
442 /// Get the directly-connected server
443 /// @result the server, or nullptr if a communication connection
444 Server
*GetServer () const
446 return is_direct
? server
: nullptr;
451 /// Perform connection handshake. All othe requests will result in
452 /// errors, until handshake is succesful.
453 /// @param agent compiler identification
454 /// @param ident compilation identifiation (maybe nullptr)
455 /// @param alen length of agent string, if known
456 /// @param ilen length of ident string, if known
457 /// @result packet indicating success (or deferrment) of the
458 /// connection, payload is optional flags
459 Packet
Connect (char const *agent
, char const *ident
,
460 size_t alen
= ~size_t (0), size_t ilen
= ~size_t (0));
461 /// std::string wrapper for connection
462 /// @param agent compiler identification
463 /// @param ident compilation identification
464 Packet
Connect (std::string
const &agent
, std::string
const &ident
)
466 return Connect (agent
.c_str (), ident
.c_str (),
467 agent
.size (), ident
.size ());
471 /// Request compiler module repository
472 /// @result packet indicating repo
473 Packet
ModuleRepo ();
476 /// Inform of compilation of a named module interface or partition,
478 /// @param str module or header-unit
479 /// @param len name length, if known
480 /// @result CMI name (or deferrment/error)
481 Packet
ModuleExport (char const *str
, Flags flags
, size_t len
= ~size_t (0));
483 Packet
ModuleExport (char const *str
)
485 return ModuleExport (str
, Flags::None
, ~size_t (0));
487 Packet
ModuleExport (std::string
const &s
, Flags flags
= Flags::None
)
489 return ModuleExport (s
.c_str (), flags
, s
.size ());
493 /// Importation of a module, partition or header-unit
494 /// @param str module or header-unit
495 /// @param len name length, if known
496 /// @result CMI name (or deferrment/error)
497 Packet
ModuleImport (char const *str
, Flags flags
, size_t len
= ~size_t (0));
499 Packet
ModuleImport (char const *str
)
501 return ModuleImport (str
, Flags::None
, ~size_t (0));
503 Packet
ModuleImport (std::string
const &s
, Flags flags
= Flags::None
)
505 return ModuleImport (s
.c_str (), flags
, s
.size ());
509 /// Successful compilation of a module interface, partition or
510 /// header-unit. Must have been preceeded by a ModuleExport
512 /// @param str module or header-unit
513 /// @param len name length, if known
514 /// @result OK (or deferment/error)
515 Packet
ModuleCompiled (char const *str
, Flags flags
, size_t len
= ~size_t (0));
517 Packet
ModuleCompiled (char const *str
)
519 return ModuleCompiled (str
, Flags::None
, ~size_t (0));
521 Packet
ModuleCompiled (std::string
const &s
, Flags flags
= Flags::None
)
523 return ModuleCompiled (s
.c_str (), flags
, s
.size ());
526 /// Include translation query.
527 /// @param str header unit name
528 /// @param len name length, if known
529 /// @result Packet indicating include translation boolean, or CMI
530 /// name (or deferment/error)
531 Packet
IncludeTranslate (char const *str
, Flags flags
,
532 size_t len
= ~size_t (0));
534 Packet
IncludeTranslate (char const *str
)
536 return IncludeTranslate (str
, Flags::None
, ~size_t (0));
538 Packet
IncludeTranslate (std::string
const &s
, Flags flags
= Flags::None
)
540 return IncludeTranslate (s
.c_str (), flags
, s
.size ());
544 /// Cork the connection. All requests are queued up. Each request
545 /// call will return a PC_CORKED packet.
548 /// Uncork the connection. All queued requests are sent to the
549 /// server, and a block of responses waited for.
550 /// @result A vector of packets, containing the in-order responses to the
552 std::vector
<Packet
> Uncork ();
554 /// Indicate corkedness of connection
555 bool IsCorked () const
557 return !corked
.empty ();
561 Packet
ProcessResponse (std::vector
<std::string
> &, unsigned code
,
563 Packet
MaybeRequest (unsigned code
);
564 int CommunicateWithServer ();
567 /// This server-side class is used to resolve requests from one or
568 /// more clients. You are expected to derive from it and override the
569 /// virtual functions it provides. The connection resolver may return
570 /// a different resolved object to service the remainder of the
571 /// connection -- for instance depending on the compiler that is
572 /// making the requests.
576 Resolver () = default;
577 virtual ~Resolver ();
580 /// Mapping from a module or header-unit name to a CMI file name.
581 /// @param module module name
583 virtual std::string
GetCMIName (std::string
const &module
);
585 /// Return the CMI file suffix to use
586 /// @result CMI suffix, a statically allocated string
587 virtual char const *GetCMISuffix ();
590 /// When the requests of a directly-connected server are processed,
591 /// we may want to wait for the requests to complete (for instance a
593 /// @param s directly connected server.
594 virtual void WaitUntilReady (Server
*s
);
597 /// Provide an error response.
598 /// @param s the server to provide the response to.
599 /// @param msg the error message
600 virtual void ErrorResponse (Server
*s
, std::string
&&msg
);
603 /// Connection handshake. Provide response to server and return new
604 /// (or current) resolver, or nullptr.
605 /// @param s server to provide response to
606 /// @param version the client's version number
607 /// @param agent the client agent (compiler identification)
608 /// @param ident the compilation identification (may be empty)
609 /// @result nullptr in the case of an error. An error response will
610 /// be sent. If handing off to another resolver, return that,
612 virtual Resolver
*ConnectRequest (Server
*s
, unsigned version
,
613 std::string
&agent
, std::string
&ident
);
616 // return 0 on ok, ERRNO on failure, -1 on unspecific error
617 virtual int ModuleRepoRequest (Server
*s
);
619 virtual int ModuleExportRequest (Server
*s
, Flags flags
,
620 std::string
&module
);
621 virtual int ModuleImportRequest (Server
*s
, Flags flags
,
622 std::string
&module
);
623 virtual int ModuleCompiledRequest (Server
*s
, Flags flags
,
624 std::string
&module
);
625 virtual int IncludeTranslateRequest (Server
*s
, Flags flags
,
626 std::string
&include
);
630 /// This server-side (build system) class handles a single connection
631 /// to a client. It has 3 states, READING:accumulating a message
632 /// block froma client, WRITING:writing a message block to a client
633 /// and PROCESSING:resolving requests. If the server does not spawn
634 /// jobs to build needed artifacts, the PROCESSING state will be brief.
640 READING
, // Server is waiting for completion of a (set of)
641 // requests from client. The next state will be PROCESSING.
642 WRITING
, // Server is writing a (set of) responses to client.
643 // The next state will be READING.
644 PROCESSING
// Server is processing client request(s). The next
645 // state will be WRITING.
649 Detail::MessageBuffer write
;
650 Detail::MessageBuffer read
;
653 bool is_connected
= false;
654 Direction direction
: 2;
657 Server (Resolver
*r
);
658 Server (Resolver
*r
, int from
, int to
= -1)
662 fd
.to
= to
>= 0 ? to
: from
;
666 Server
&operator= (Server
&&);
669 bool IsConnected () const
675 void SetDirection (Direction d
)
681 Direction
GetDirection () const
685 int GetFDRead () const
689 int GetFDWrite () const
693 Resolver
*GetResolver () const
699 /// Process requests from a directly-connected client. This is a
700 /// small wrapper around ProcessRequests, with some buffer swapping
701 /// for communication. It is expected that such processessing is
703 /// @param from message block from client
704 /// @param to message block to client
705 void DirectProcess (Detail::MessageBuffer
&from
, Detail::MessageBuffer
&to
);
708 /// Process the messages queued in the read buffer. We enter the
709 /// PROCESSING state, and each message line causes various resolver
710 /// methods to be called. Once processed, the server may need to
711 /// wait for all the requests to be ready, or it may be able to
712 /// immediately write responses back.
713 void ProcessRequests ();
716 /// Accumulate an error response.
717 /// @param error the error message to encode
718 /// @param elen length of error, if known
719 void ErrorResponse (char const *error
, size_t elen
= ~size_t (0));
720 void ErrorResponse (std::string
const &error
)
722 ErrorResponse (error
.data (), error
.size ());
725 /// Accumulate an OK response
728 /// Accumulate a boolean response
729 void BoolResponse (bool);
731 /// Accumulate a pathname response
732 /// @param path (may be nullptr, or empty)
733 /// @param rlen length, if known
734 void PathnameResponse (char const *path
, size_t plen
= ~size_t (0));
735 void PathnameResponse (std::string
const &path
)
737 PathnameResponse (path
.data (), path
.size ());
741 /// Accumulate a (successful) connection response
742 /// @param agent the server-side agent
743 /// @param alen agent length, if known
744 void ConnectResponse (char const *agent
, size_t alen
= ~size_t (0));
745 void ConnectResponse (std::string
const &agent
)
747 ConnectResponse (agent
.data (), agent
.size ());
751 /// Write message block to client. Semantics as for
752 /// MessageBuffer::Write.
753 /// @result errno or completion (0).
756 return write
.Write (fd
.to
);
758 /// Initialize for writing a message block. All responses to the
759 /// incomping message block must be complete Enters WRITING state.
760 void PrepareToWrite ()
762 write
.PrepareToWrite ();
767 /// Read message block from client. Semantics as for
768 /// MessageBuffer::Read.
769 /// @result errno, eof (-1) or completion (0)
772 return read
.Read (fd
.from
);
774 /// Initialize for reading a message block. Enters READING state.
775 void PrepareToRead ()
777 read
.PrepareToRead ();
782 // Helper network stuff
785 // Socket with specific address
786 int OpenSocket (char const **, sockaddr
const *sock
, socklen_t len
);
787 int ListenSocket (char const **, sockaddr
const *sock
, socklen_t len
,
790 // Local domain socket (eg AF_UNIX)
791 int OpenLocal (char const **, char const *name
);
792 int ListenLocal (char const **, char const *name
, unsigned backlog
= 0);
795 int OpenInet6 (char const **e
, char const *name
, int port
);
796 int ListenInet6 (char const **, char const *name
, int port
,
797 unsigned backlog
= 0);
800 // FIXME: Mapping file utilities?