1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00
putty-source/sshcr.h

88 lines
3.2 KiB
C
Raw Normal View History

/*
* Coroutine mechanics used in PuTTY's SSH code.
*/
#ifndef PUTTY_SSHCR_H
#define PUTTY_SSHCR_H
/*
* If these macros look impenetrable to you, you might find it helpful
* to read
*
* https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
*
* which explains the theory behind these macros.
*
* In particular, if you are getting `case expression not constant'
* errors when building with MS Visual Studio, this is because MS's
* Edit and Continue debugging feature causes their compiler to
* violate ANSI C. To disable Edit and Continue debugging:
*
* - right-click ssh.c in the FileView
* - click Settings
* - select the C/C++ tab and the General category
* - under `Debug info:', select anything _other_ than `Program
* Database for Edit and Continue'.
*/
#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
#define crBeginState crBegin(s->crLine)
#define crStateP(t, v) \
struct t *s; \
if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \
s = (v);
#define crState(t) crStateP(t, ssh->t)
#define crFinish(z) } *crLine = 0; return (z); }
#define crFinishV } *crLine = 0; return; }
Pageant: new asynchronous internal APIs. This is a pure refactoring: no functional change expected. This commit introduces two new small vtable-style APIs. One is PageantClient, which identifies a particular client of the Pageant 'core' (meaning the code that handles each individual request). This changes pageant_handle_msg into an asynchronous operation: you pass in an agent request message and an identifier, and at some later point, the got_response method in your PageantClient will be called with the answer (and the same identifier, to allow you to match requests to responses). The trait vtable also contains a logging system. The main importance of PageantClient, and the reason why it has to exist instead of just passing pageant_handle_msg a bare callback function pointer and context parameter, is that it provides robustness if a client stops existing while a request is still pending. You call pageant_unregister_client, and any unfinished requests associated with that client in the Pageant core will be cleaned up, so that you're guaranteed that after the unregister operation, no stray callbacks will happen with a stale pointer to that client. The WM_COPYDATA interface of Windows Pageant is a direct client of this API. The other client is PageantListener, the system that lives in pageant.c and handles stream-based agent connections for both Unix Pageant and the new Windows named-pipe IPC. More specifically, each individual connection to the listening socket is a separate PageantClient, which means that if a socket is closed abruptly or suffers an OS error, that client can be unregistered and any pending requests cancelled without disrupting other connections. Users of PageantListener have a second client vtable they can use, called PageantListenerClient. That contains _only_ logging facilities, and at the moment, only Unix Pageant bothers to use it (and even that only in debugging mode). Finally, internally to the Pageant core, there's a new trait called PageantAsyncOp which describes an agent request in the process of being handled. But at the moment, it has only one trivial implementation, which is handed the full response message already constructed, and on the next toplevel callback, passes it back to the PageantClient.
2020-01-25 17:24:28 +00:00
#define crFinishFreed(z) } return (z); }
#define crFinishFreedV } return; }
#define crFinishFree(z) } sfree(s); return (z); }
#define crFinishFreeV } sfree(s); return; }
#define crReturn(z) \
do {\
*crLine =__LINE__; return (z); case __LINE__:;\
} while (0)
#define crReturnV \
do {\
*crLine=__LINE__; return; case __LINE__:;\
} while (0)
#define crStop(z) do{ *crLine = 0; return (z); }while(0)
#define crStopV do{ *crLine = 0; return; }while(0)
/*
* The crMaybeWaitUntil macros could have been more easily written in
* terms of the simple crReturn above, by writing things like
*
* while (!condition) { crReturn(whatever); }
*
* (or do-while in the case of crWaitUntil). But it's better to do it
* directly by writing _once_ to crLine before first testing the
* condition, because this way it's robust against the condition check
* potentially freeing the entire coroutine state structure as a side
* effect (as long as it also evaluates false if it does that),
* because we don't write into crLine between the condition evaluating
* to false and the 'return' statement.
*/
#define crMaybeWaitUntil(c) \
do { \
*crLine =__LINE__; \
case __LINE__: if (!(c)) return 0; \
} while (0)
#define crMaybeWaitUntilV(c) \
do { \
*crLine =__LINE__; \
case __LINE__: if (!(c)) return; \
} while (0)
#define crWaitUntil(c) \
do { \
*crLine =__LINE__; return; \
case __LINE__: if (!(c)) return 0; \
} while (0)
#define crWaitUntilV(c) \
do { \
*crLine =__LINE__; return; \
case __LINE__: if (!(c)) return; \
} while (0)
#endif /* PUTTY_SSHCR_H */