From: "Nickle Wang via groups.io" <nicklew=nvidia.com@groups.io>
To: <devel@edk2.groups.io>
Cc: Abner Chang <abner.chang@amd.com>,
Igor Kulchytskyy <igork@ami.com>,
"Nick Ramirez" <nramirez@nvidia.com>
Subject: [edk2-devel] [edk2-redfish-client][PATCH] RedfishClientPkg: introduce RedfishBootstrapAccountDxe
Date: Thu, 18 Apr 2024 20:27:30 +0800 [thread overview]
Message-ID: <20240418122730.18204-1-nicklew@nvidia.com> (raw)
-Introduce RedfishBootstrapAccountDxe to delete bootstrap
account from /redfish/v1/AccountService/Accounts after BIOS
finished all Redfish jobs. The bootstrap account won't be
available to other application. So deleting bootstrap account
helps to release resource at BMC.
- After bootstrap account is deleted at BMC, the Redfish service
instance is no longer usable. Close Redfish service instance to
release the HTTP connection between BIOS and BMC.
Signed-off-by: Nickle Wang <nicklew@nvidia.com>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Igor Kulchytskyy <igork@ami.com>
Cc: Nick Ramirez <nramirez@nvidia.com>
---
.../RedfishClientComponents.dsc.inc | 1 +
.../RedfishBootstrapAccountDxe.inf | 53 +++
.../RedfishBootstrapAccountDxe.h | 58 ++++
.../RedfishBootstrapAccountDxe.c | 328 ++++++++++++++++++
RedfishClientPkg/RedfishClient.fdf.inc | 1 +
5 files changed, 441 insertions(+)
create mode 100644 RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf
create mode 100644 RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.h
create mode 100644 RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.c
diff --git a/RedfishClientPkg/RedfishClientComponents.dsc.inc b/RedfishClientPkg/RedfishClientComponents.dsc.inc
index 42fc0c299..fe5248b62 100644
--- a/RedfishClientPkg/RedfishClientComponents.dsc.inc
+++ b/RedfishClientPkg/RedfishClientComponents.dsc.inc
@@ -20,6 +20,7 @@
RedfishClientPkg/HiiToRedfishMemoryDxe/HiiToRedfishMemoryDxe.inf
RedfishClientPkg/HiiToRedfishBootDxe/HiiToRedfishBootDxe.inf
RedfishClientPkg/HiiToRedfishBiosDxe/HiiToRedfishBiosDxe.inf
+ RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf
!endif
#
# Below two modules should be pulled in by build tool.
diff --git a/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf
new file mode 100644
index 000000000..4073e95f4
--- /dev/null
+++ b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf
@@ -0,0 +1,53 @@
+## @file
+# This driver deletes bootstrap account in BMC after BIOS Redfish finished
+# all jobs
+#
+# (C) Copyright 2021 Hewlett Packard Enterprise Development LP<BR>
+# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001000b
+ BASE_NAME = RedfishBootstrapAccountDxe
+ FILE_GUID = 87555253-2F7E-45FC-B469-FD35B2E51210
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = RedfishBootstrapAccountEntryPoint
+ UNLOAD_IMAGE = RedfishBootstrapAccountUnload
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ RedfishPkg/RedfishPkg.dec
+ RedfishClientPkg/RedfishClientPkg.dec
+
+[Sources]
+ RedfishBootstrapAccountDxe.h
+ RedfishBootstrapAccountDxe.c
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ PrintLib
+ RedfishEventLib
+ RedfishFeatureUtilityLib
+ RedfishDebugLib
+ RedfishVersionLib
+ RedfishHttpLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEdkIIRedfishConfigHandlerProtocolGuid ## CONSUMES ##
+ gEdkIIRedfishCredentialProtocolGuid ## CONSUMES ##
+ gEfiRestExProtocolGuid ## CONSUMES ##
+
+[Depex]
+ gEdkIIRedfishCredentialProtocolGuid
diff --git a/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.h b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.h
new file mode 100644
index 000000000..5262f1e6b
--- /dev/null
+++ b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.h
@@ -0,0 +1,58 @@
+/** @file
+ Common header file for RedfishBootstrapAccountDxe driver.
+
+ (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
+ Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef REDFISH_BOOTSTRAP_ACCOUNT_DXE_H_
+#define REDFISH_BOOTSTRAP_ACCOUNT_DXE_H_
+
+#include <Uefi.h>
+#include <RedfishBase.h>
+
+//
+// Libraries
+//
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/RedfishEventLib.h>
+#include <Library/RedfishFeatureUtilityLib.h>
+#include <Library/RedfishDebugLib.h>
+#include <Library/RedfishVersionLib.h>
+#include <Library/RedfishHttpLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Protocol/EdkIIRedfishConfigHandler.h>
+#include <Protocol/EdkIIRedfishCredential.h>
+#include <Protocol/RestEx.h>
+
+#define REDFISH_BOOTSTRAP_ACCOUNT_DEBUG DEBUG_VERBOSE
+#define REDFISH_MANAGER_ACCOUNT_COLLECTION_URI L"AccountService/Accounts"
+#define REDFISH_URI_LENGTH 128
+
+//
+// Definitions of REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE
+//
+typedef struct {
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE RestExHandle;
+ REDFISH_SERVICE RedfishService;
+ EFI_EVENT RedfishEvent;
+ EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL Protocol;
+} REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE;
+
+#define REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE_FROM_PROTOCOL(This) \
+ BASE_CR ((This), REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE, Protocol)
+
+#endif
diff --git a/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.c b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.c
new file mode 100644
index 000000000..6fe4856f8
--- /dev/null
+++ b/RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.c
@@ -0,0 +1,328 @@
+/** @file
+ This driver deletes bootstrap account in BMC after BIOS Redfish finished
+ all jobs.
+
+ (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
+ Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishBootstrapAccountDxe.h"
+
+REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE *mBootstrapPrivate = NULL;
+
+/**
+ Close Redfish service instance by calling RestEx protocol to release instance.
+
+ @param[in] RestExHandle Handle of RestEx protocol.
+
+ @retval EFI_SUCCESS The Redfish service is closed successfully.
+ @retval EFI_INVALID_PARAMETER RestExHandle is NULL.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+CloseRedfishService (
+ IN EFI_HANDLE RestExHandle
+ )
+{
+ EFI_REST_EX_PROTOCOL *RestEx;
+ EFI_STATUS Status;
+
+ if (RestExHandle == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->HandleProtocol (
+ RestExHandle,
+ &gEfiRestExProtocolGuid,
+ (VOID **)&RestEx
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = RestEx->Configure (RestEx, NULL);
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: release RestEx instance: %r\n", __func__, Status));
+ }
+
+ return Status;
+}
+
+/**
+ Callback function executed when the AfterProvisioning event group is signaled.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[out] Context Pointer to the Context buffer
+
+**/
+VOID
+EFIAPI
+RedfishBootstrapAccountOnRedfishAfterProvisioning (
+ IN EFI_EVENT Event,
+ OUT VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE *Private;
+ EDKII_REDFISH_CREDENTIAL_PROTOCOL *credentialProtocol;
+ EDKII_REDFISH_AUTH_METHOD AuthMethod;
+ CHAR8 *AccountName;
+ CHAR8 *AccountCredential;
+ CHAR16 TargetUri[REDFISH_URI_LENGTH];
+ CHAR16 *RedfishVersion;
+ REDFISH_RESPONSE RedfishResponse;
+
+ RedfishVersion = NULL;
+
+ Private = (REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE *)Context;
+ if ((Private == NULL) || (Private->RedfishService == NULL)) {
+ DEBUG ((DEBUG_ERROR, "%a: Redfish service is not available\n", __func__));
+ return;
+ }
+
+ //
+ // Locate Redfish Credential Protocol to get credential for
+ // accessing to Redfish service.
+ //
+ Status = gBS->LocateProtocol (
+ &gEdkIIRedfishCredentialProtocolGuid,
+ NULL,
+ (VOID **)&credentialProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: No Redfish Credential Protocol is installed on system.", __func__));
+ return;
+ }
+
+ Status = credentialProtocol->GetAuthInfo (
+ credentialProtocol,
+ &AuthMethod,
+ &AccountName,
+ &AccountCredential
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: can not get bootstrap account information: %r\n", __func__, Status));
+ return;
+ }
+
+ //
+ // Carving the URI
+ //
+ RedfishVersion = RedfishGetVersion (Private->RedfishService);
+ if (RedfishVersion == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: can not get Redfish version\n", __func__));
+ return;
+ }
+
+ UnicodeSPrint (TargetUri, (sizeof (CHAR16) * REDFISH_URI_LENGTH), L"%s%s/%a", RedfishVersion, REDFISH_MANAGER_ACCOUNT_COLLECTION_URI, AccountName);
+
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: bootstrap account: %a\n", __func__, AccountName));
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: bootstrap credential: %a\n", __func__, AccountCredential));
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: bootstrap URI: %s\n", __func__, TargetUri));
+
+ //
+ // Remove bootstrap account at /redfish/v1/AccountService/Account
+ //
+ ZeroMem (&RedfishResponse, sizeof (REDFISH_RESPONSE));
+ Status = RedfishHttpDeleteResource (
+ Private->RedfishService,
+ TargetUri,
+ &RedfishResponse
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: can not remove bootstrap account at BMC: %r", __func__, Status));
+ DumpRedfishResponse (__func__, DEBUG_ERROR, &RedfishResponse);
+ } else {
+ DEBUG ((REDFISH_BOOTSTRAP_ACCOUNT_DEBUG, "%a: bootstrap account: %a is removed from: %s\n", __func__, AccountName, REDFISH_MANAGER_ACCOUNT_COLLECTION_URI));
+ }
+
+ //
+ // Clean credential
+ //
+ ZeroMem (AccountName, AsciiStrSize (AccountName));
+ ZeroMem (AccountCredential, AsciiStrSize (AccountCredential));
+
+ //
+ // Since the bootstrap account is deleted at BMC, the Redfish service instance is no longer usable.
+ // Close Redfish service instance to release the HTTP connection between BIOS and BMC.
+ //
+ Status = CloseRedfishService (Private->RestExHandle);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: cannot close Redfish service instance: %r\n", __func__, Status));
+ }
+
+ RedfishHttpFreeResponse (&RedfishResponse);
+
+ return;
+}
+
+/**
+ Initialize a Redfish configure handler.
+
+ This function will be called by the Redfish config driver to initialize each Redfish configure
+ handler.
+
+ @param[in] This Pointer to EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL instance.
+ @param[in] RedfishConfigServiceInfo Redfish service informaiton.
+
+ @retval EFI_SUCCESS The handler has been initialized successfully.
+ @retval EFI_DEVICE_ERROR Failed to create or configure the REST EX protocol instance.
+ @retval EFI_ALREADY_STARTED This handler has already been initialized.
+ @retval Other Error happens during the initialization.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishBootstrapAccountInit (
+ IN EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL *This,
+ IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
+ )
+{
+ REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE *Private;
+
+ Private = REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE_FROM_PROTOCOL (This);
+
+ Private->RedfishService = RedfishCreateService (RedfishConfigServiceInfo);
+ if (Private->RedfishService == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private->RestExHandle = RedfishConfigServiceInfo->RedfishServiceRestExHandle;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Stop a Redfish configure handler.
+
+ @param[in] This Pointer to EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL instance.
+
+ @retval EFI_SUCCESS This handler has been stoped successfully.
+ @retval Others Some error happened.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishBootstrapAccountStop (
+ IN EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL *This
+ )
+{
+ REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE *Private;
+
+ Private = REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE_FROM_PROTOCOL (This);
+
+ if (Private->RedfishService != NULL) {
+ RedfishCleanupService (Private->RedfishService);
+ Private->RedfishService = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL mRedfishConfigHandler = {
+ RedfishBootstrapAccountInit,
+ RedfishBootstrapAccountStop
+};
+
+/**
+ Unloads an image.
+
+ @param[in] ImageHandle Handle that identifies the image to be unloaded.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishBootstrapAccountUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+
+ if (mBootstrapPrivate == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ if (mBootstrapPrivate->RedfishEvent != NULL) {
+ gBS->CloseEvent (mBootstrapPrivate->RedfishEvent);
+ }
+
+ Status = gBS->UninstallProtocolInterface (
+ mBootstrapPrivate->ImageHandle,
+ &gEdkIIRedfishConfigHandlerProtocolGuid,
+ (VOID *)&mBootstrapPrivate->Protocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: can not uninstall Redfish config handler protocol: %r\n", __func__, Status));
+ }
+
+ FreePool (mBootstrapPrivate);
+ mBootstrapPrivate = NULL;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others An unexpected error occurred.
+**/
+EFI_STATUS
+EFIAPI
+RedfishBootstrapAccountEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ if (mBootstrapPrivate != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ mBootstrapPrivate = AllocateZeroPool (sizeof (REDFISH_BOOTSTRAP_ACCOUNT_PRIVATE));
+ if (mBootstrapPrivate == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&mBootstrapPrivate->Protocol, &mRedfishConfigHandler, sizeof (EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL));
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEdkIIRedfishConfigHandlerProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mBootstrapPrivate->Protocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: can not install Redfish config handler protocol: %r\n", __func__, Status));
+ goto ON_ERROR;
+ }
+
+ //
+ // Register after provisioning event to remove bootstrap account.
+ //
+ Status = CreateAfterProvisioningEvent (
+ RedfishBootstrapAccountOnRedfishAfterProvisioning,
+ (VOID *)mBootstrapPrivate,
+ &mBootstrapPrivate->RedfishEvent
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to register after-provisioning event: %r\n", __func__, Status));
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ RedfishBootstrapAccountUnload (ImageHandle);
+
+ return Status;
+}
diff --git a/RedfishClientPkg/RedfishClient.fdf.inc b/RedfishClientPkg/RedfishClient.fdf.inc
index 154f641b2..47e5093f2 100644
--- a/RedfishClientPkg/RedfishClient.fdf.inc
+++ b/RedfishClientPkg/RedfishClient.fdf.inc
@@ -15,6 +15,7 @@
INF RedfishClientPkg/RedfishFeatureCoreDxe/RedfishFeatureCoreDxe.inf
INF RedfishClientPkg/RedfishETagDxe/RedfishETagDxe.inf
INF RedfishClientPkg/RedfishConfigLangMapDxe/RedfishConfigLangMapDxe.inf
+ INF RedfishClientPkg/RedfishBootstrapAccountDxe/RedfishBootstrapAccountDxe.inf
INF RedfishClientPkg/Features/Memory/V1_7_1/Dxe/MemoryDxe.inf
INF RedfishClientPkg/Features/MemoryCollectionDxe/MemoryCollectionDxe.inf
INF RedfishClientPkg/Features/ComputerSystem/v1_5_0/Dxe/ComputerSystemDxe.inf
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#117986): https://edk2.groups.io/g/devel/message/117986
Mute This Topic: https://groups.io/mt/105596648/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next reply other threads:[~2024-04-18 12:28 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-18 12:27 Nickle Wang via groups.io [this message]
2024-04-22 2:24 ` [edk2-devel] [edk2-redfish-client][PATCH] RedfishClientPkg: introduce RedfishBootstrapAccountDxe Chang, Abner via groups.io
2024-04-22 15:02 ` Igor Kulchytskyy via groups.io
2024-04-23 7:09 ` Nickle Wang via groups.io
2024-04-23 7:42 ` Chang, Abner via groups.io
2024-05-14 12:40 ` Nickle Wang via groups.io
2024-05-15 15:01 ` Igor Kulchytskyy via groups.io
2024-05-16 1:05 ` Chang, Abner via groups.io
2024-05-16 1:37 ` Nickle Wang via groups.io
2024-05-16 2:41 ` Chang, Abner via groups.io
2024-05-16 3:09 ` Nickle Wang via groups.io
2024-05-16 11:33 ` Igor Kulchytskyy via groups.io
2024-05-17 7:17 ` Nickle Wang via groups.io
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240418122730.18204-1-nicklew@nvidia.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox