Jump to content


p4api.net Connection Through Web Site Failing


  • Please log in to reply
15 replies to this topic

#1 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 03 September 2013 - 11:09 PM

Code fails with the folllowing error when trying to connect to Perforce through an ASP.NET Web Application.  I pull the same code out of the Web App and put it in a Windows App deployed to the same server and it is successful.  Only going through a Web App causes this on the server (Web App works locally on my development machine).

Using the version "Perforce Visual Client/NTX86/2009.1/205670" and p4api.net.dll version "2013.2.66.1822", Windows Server 2008 R2, IIS 7.5, .NET 4.0

************************ LOG ************************
Logger: Interface
Thread: 13
Date: 2013-08-20 11:55:47,841

WW00065E :: DEBUG :: Connection.Status=Connected

Logger: Interface
Thread: 13
Date: 2013-08-20 11:55:48,434

WW00065E :: DEBUG :: Server.State=Online

Logger: Interface
Thread: 13
Date: 2013-08-20 11:55:48,434

WW00065E :: ERROR :: Error Connecting to Perforce
Fatal client error; disconnecting!
open for write: C:\windows\system32\config\systemprofile\t2704t95.tmp: Access is denied
.


at Perforce.P4.P4Exception.Throw(String cmd, String[] args, P4ClientErrorList errors, String[] details) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\P4Exception.cs:line 376
at Perforce.P4.P4Server.RunCommand(String cmd, UInt32 cmdId, Boolean tagged, String[] args, Int32 argc) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\P4Server.cs:line 1182
at Perforce.P4.P4Command.RunInt(StringList flags) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\P4Command.cs:line 1048
at Perforce.P4.P4CommandResult..ctor(P4Command cmd, StringList flags) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\P4CommandResult.cs:line 142
at Perforce.P4.P4Command.Run(StringList flags) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\P4Command.cs:line 443
at Perforce.P4.Connection.Login(String password) in c:\tmp\28117053\src\P4.NET\r13.2\p4api.net\p4api.net\Connection.cs:line 706
at Abbott.Vascular.CATSWeb.PerforceAPI.Connect() in C:\Perforce\nsnyder_prod\depot\QSIT_projects\CATSWeb\4_ENG\trunk\src\AVChangeRequestForms\PerforceAPI.cs:line 69

Logger: Interface
Thread: 13
Date: 2013-08-20 11:55:48,792

************************ CODE SNIPPET ************************

string serverAddr = appSettingsSection.Settings["PerforceServer"].Value.ToString();
string user = appSettingsSection.Settings["UserServiceAccount"].Value.ToString();
string pword = CryptographyManager.Decrypt(appSettingsSection.Settings["UserServiceAccountPwd"].Value.ToString(), CONFIGPASS);
string workspace = appSettingsSection.Settings["P4ConfigWorkspace"].Value.ToString();

Perforce.P4.Server svr = new Perforce.P4.Server(new Perforce.P4.ServerAddress(serverAddr));
Perforce.P4.Repository p4Rep = new Perforce.P4.Repository(svr);

// Connect to Perforce server
p4Rep.Connection.UserName = user;
p4Rep.Connection.Client = new Perforce.P4.Client();
p4Rep.Connection.Connect(null);

// Connection Status = Connected
log.Debug("Connection.Status=" + p4Rep.Connection.Status);

// Server State = Online
log.Debug("Server.State=" + p4Rep.Connection.Server.State);

// Login user - error at this line
Perforce.P4.Credential cred = p4Rep.Connection.Login(pword);


if (cred == null)
{
log.Debug("cred = null");
}

p4Rep.Connection.Credential = cred;

// set workspace
p4Rep.Connection.Client = p4Rep.GetClient(workspace);

log.Debug("workspace=" + p4Rep.Connection.Client.Name);

#2 P4Matt

P4Matt

    Advanced Member

  • Members
  • PipPipPip
  • 1383 posts

Posted 03 September 2013 - 11:17 PM

Does the account running the application have permission to write to the tmp directory? It looks like a file permissions error to me.

#3 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 03 September 2013 - 11:25 PM

Yes, I even gave "Everyone" full control to the directories and made sure I was part of the Administrators group.  Still no luck.  Do you have any code working through the web like this on Windows Server 2008 IIS 7.5?

#4 P4Matt

P4Matt

    Advanced Member

  • Members
  • PipPipPip
  • 1383 posts

Posted 03 September 2013 - 11:39 PM

Not that I know of; everything we've written has been for more standard C# apps.

The fact that the web app works in dev mode is pretty interesting. That says to me there is something different about the running environment.

The problem is likely occurring because Perforce is trying to write out the ticket file after logging in. A workaround might be to use the '-p' flag on login. That causes Perforce to print out the ticket value instead of storing it. Another option would be to make sure that TEMP is set to something that the application is capable of writing to; I'm pretty sure nothing should be writing temp files to sys32.

#5 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 04 September 2013 - 04:29 PM

I changed my code to use the "-p" flag like the following and it doesn't error at login now but returns a null Credential.  If login is successful it should always return a Perforce.P4.Credential right?  Also, by TEMP I'm assuming you mean the TEMP environment variable and that is not set to sys32 (let me know if you meant something else).

Perforce.P4.Options opts = new Perforce.P4.Options(Perforce.P4.LoginCmdFlags.DisplayTicket, server);
Perforce.P4.Credential cred = p4Rep.Connection.Login(pword, opts);

if (cred == null)
{

log.Debug("cred = null");

}

p4Rep.Connection.Credential = cred;


#6 P4Matt

P4Matt

    Advanced Member

  • Members
  • PipPipPip
  • 1383 posts

Posted 04 September 2013 - 05:28 PM

I would certainly expect it to. Looking at the code however, it throws it away. You can easily re-create the method yourself. Below is the code for the login method from the .net API. After the RunInt() call you should be able to call InfoOutput() to get the generated ticket value. Then you should be able to store that ticket value wherever is convenient for you. You'll want to call Set() on your P4Server object to set P4PASSWD to that ticket value. That should get you to the point you can run Perforce commands successfully without the weird temp file issue you're seeing.


		public bool Login(string password, StringList options)
		{
			if (!requiresLogin)
			{
				// server does not support login command
				if (!string.IsNullOrEmpty(password))
				{
					using (PinnedByteArray pPass = MarshalStringToIntPtr(password))
					{
						P4Bridge.set_passwordW(pServer, pPass);
					}
					return true;
				}
				return false;
			}
			P4Command login = new P4Command(this, "login", false);
			login.Responses = new Dictionary<string, string>();
			login.Responses["DefaultResponse"] = password;
			bool results = false;
			try
			{
				results = login.RunInt(options);
			}
			catch //(Exception ex)
			{
				results = false;
			}
			if (!results)
			{
				Connectionerror = _errorList[0].ErrorMessage;
			}
			else
			{
				Connectionerror = null;
			}
			return results;
		}


#7 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 04 September 2013 - 06:39 PM

Code won't compile, getting error "no definition for RunInt".  There is a Run method but that returns a "P4CommandResults" object.  Is there some other P4Command or is the RunInt method only on certain versions of the API?  Also, after using this login method how do I use the "Repository" object to "GetClient" or any of those methods?

'Perforce.P4.P4Command' does not contain a definition for 'RunInt' and no extension method 'RunInt' accepting a first argument of type 'Perforce.P4.P4Command' could be found (are you missing a using directive or an assembly reference?)


			Perforce.P4.P4Server svr = new Perforce.P4.P4Server(server, user, pword, workspace);
			Perforce.P4.P4Command login = new Perforce.P4.P4Command(svr, "login", false);
			login.Responses = new Dictionary<string, string>();
			login.Responses["DefaultResponse"] = pword;
			bool results = false;
			try
			{
				results = login.RunInt(options);
			}
			catch //(Exception ex)
			{
				results = false;
			}
			if (!results)
			{
				log.Debug("login failure");
			}
			string ticket = login.InfoOutput;
			log.Debug("ticket=" + ticket);
			svr.Set("P4PASSWD", ticket);


#8 P4Matt

P4Matt

    Advanced Member

  • Members
  • PipPipPip
  • 1383 posts

Posted 04 September 2013 - 07:44 PM

That Run() method should do the trick. The P4CommandResults object should have the information you are after in it; calling its InfoOutput() method should return the ticket value. Once you have that you should be able to call svr.Password = <the ticket value you just received>.

Once that is set you should be able to use any of the methods on the Repository object to execute commands against the server. For instance:

// initialize the connection variables
// note: this is a connection without using a password
string uri = "localhost:6666";
string user = "admin";
string ws_client = "admin_space";
// define the server, repository and connection
Server server = new Server(new ServerAddress(uri));
Repository rep = new Repository(server);
Connection con = rep.Connection;
// use the connection varaibles for this connection
con.UserName = user;
con.Client = new Client();
con.Client.Name = ws_client;

// connect to the server
con.Connect(null);
// set the options for the p4 changes command
string clientName = "admin_space";
int maxItems = 5;
string userName = "admin";
Options opts = new Options(ChangesCmdFlags.LongDescription,
		clientName, maxItems, ChangeListStatus.None, userName);

// run the command against the current repository
IList<Changelist> changes = rep.getChangelists(opts);

I've also called in the big guns on this one; I have a terrible test environment for this API so I've asked the devs to chime in with their thoughts.

#9 p4bill

p4bill

    Advanced Member

  • Members
  • PipPipPip
  • 217 posts

Posted 04 September 2013 - 09:39 PM

From what I am testing here, I would have thought this would work (though I am testing directly in the .NET API unit tests):

View Postenic21, on 04 September 2013 - 04:29 PM, said:

I changed my code to use the "-p" flag like the following and it doesn't error at login now but returns a null Credential.  If login is successful it should always return a Perforce.P4.Credential right?  Also, by TEMP I'm assuming you mean the TEMP environment variable and that is not set to sys32 (let me know if you meant something else).

Perforce.P4.Options opts = new Perforce.P4.Options(Perforce.P4.LoginCmdFlags.DisplayTicket, server);
Perforce.P4.Credential cred = p4Rep.Connection.Login(pword, opts);

if (cred == null)
{

log.Debug("cred = null");

}

p4Rep.Connection.Credential = cred;



with my tests, credential is not null when login succeeds. I do get a null for credential when I change the correct "password" to "wrongpassword".

When you check for cred==null, see if an ErrorList was part of the last results:

if (cred == null)
{
P4CommandResult result = rep.Connection.LastResults;
if (result.ErrorList != null)
{
P4Exception.Throw(result.ErrorList);
}
}

If there is no error list, check result.InfoOutput for the ticket value Matt mentioned in his last post.

#10 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 05 September 2013 - 06:03 PM

This is weird.  I listed out the error and I get the following that "-h" is an invalid parameter.

[E_FAILED] Usage: login [ -a -p ] [ -s ] [ username ] Invalid option: -h.

So I listed the contents of the Options and sure enough there is a lowercase -h with the server set as the value.  I then just removed that from the options before calling login and it gave me back Credentials.  When I checked the value of cred.Ticket it shows the following (without the quotes):
"Success:  Password verified."

I checked the value of p4Rep.Connection.LastResults.InfoOutput it shows the following (without the quotes):
".....Success:  Password verified./r/n3EAFE4C286C01E4C10AFC44269059DF4/r/n"

I am able to set the Credentials so it seems like I'm logged in but when I try to set the Workspace/Client it fails with "Perforce password (P4PASSWD) invalid or unset."

p4Rep.Connection.Client = p4Rep.GetClient(workspace);


#11 p4bill

p4bill

    Advanced Member

  • Members
  • PipPipPip
  • 217 posts

Posted 05 September 2013 - 10:16 PM

The -h flag is appearing because of this line:

Perforce.P4.Options(Perforce.P4.LoginCmdFlags.DisplayTicket, server);

"server" should actually be "host" and will make the ticket valid on the specified host (IP address). I did not catch that, as my test worked with -h and a local server. Unless you want to specify a host, just set that to null.

Once you get the credential, store the ticket:

{
p4Rep.Connection._p4server.Password = cred.Ticket;
}

To set the Workspace/Client:

p4Rep.Connection.SetClient("<client name">); // <client name> is a string representing client id



#12 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 06 September 2013 - 07:16 PM

My p4Rep.Connection doesn't have a "_p4server" and is returning the following error during compile:

'Perforce.P4.Connection' does not contain a definition for '_p4server' and no extension method '_p4server' accepting a first argument of type 'Perforce.P4.Connection' could be found (are you missing a using directive or an assembly reference?)

I'm using the "Perforce.P4.Repository" object.  I may have thrown you off by naming my object "p4Rep".

#13 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 06 September 2013 - 10:09 PM

Would the following be equivilant? This isn't working but just wondering.

p4Rep.Connection.SetP4EnvironmentVar("P4PASSWD", cred.Ticket);

p4Rep.Connection.SetClient(workspace);

To

p4Rep.Connection._p4server.Password = cred.Ticket;

p4Rep.Connection.SetClient(workspace);


#14 p4bill

p4bill

    Advanced Member

  • Members
  • PipPipPip
  • 217 posts

Posted 09 September 2013 - 09:13 PM

View Postenic21, on 06 September 2013 - 10:09 PM, said:

Would the following be equivilant? This isn't working but just wondering.

p4Rep.Connection.SetP4EnvironmentVar("P4PASSWD", cred.Ticket);

p4Rep.Connection.SetClient(workspace);

To

p4Rep.Connection._p4server.Password = cred.Ticket;

p4Rep.Connection.SetClient(workspace);

That should be equivalent, and I would expect it to work. Though I just tried it here and while it changed the environment variable the next command failed. I'll look into that as a potential bug.

Apologies for suggesting

p4Rep.Connection._p4server.Password

_p4server is private, so it can work in a unit test, but not in code using the .dlls

Try this instead:

p4Rep.Connection.Credential = cred;


#15 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 12 September 2013 - 09:36 PM

Sorry for the delay, it seems there are lots of fires for me to put out this week.

My code is still failing with "Perforce password (P4PASSWD) invalid or unset." when I try to SetClient.

p4Rep.Connection.SetClient(workspace);

Below is my sample code I'm working with.  I just have a simple Web Site with a button on it that calls this code.  I also have a Windows Application with a button that calls basically the same code (message box used instead of response.write) and it fails too unless I don't pass options on login.  This is successful in the Windows Application on the same server.

Perforce.P4.Credential cred = p4Rep.Connection.Login(pword);

But that same change in the Web App on the same server throws the following error:
"Fatal client error; disconnecting! open for write: C:\windows\system32\config\systemprofile\t152t30.tmp: Access is denied.""

It's not permissions because I am an Administrator on the Server.  It says that it's "open for write" so that makes me think something has a lock on the temp file.  Why would it lock the temp file when going through the Web but not for a local Windows Application?  Some policy maybe?

		try
		{
			string server = "P4Server:1666"; // not actual server name
			string user = "P4User"; // not actual user name
			string pword = "P4Password"; // not actual password
			string workspace = "P4Workspace"; // not actual workspace or client ID
			Perforce.P4.Server svr = new Perforce.P4.Server(new Perforce.P4.ServerAddress(server));
			Perforce.P4.Repository p4Rep = new Perforce.P4.Repository(svr);
			// Connect to Perforce server
			p4Rep.Connection.UserName = user;
			p4Rep.Connection.Client = new Perforce.P4.Client();
			p4Rep.Connection.Connect(null);
			Perforce.P4.Options opts = new Perforce.P4.Options(Perforce.P4.LoginCmdFlags.DisplayTicket, null);
			// login
			Perforce.P4.Credential cred = p4Rep.Connection.Login(pword, opts);
			// log any errors and log ticket
			Perforce.P4.P4CommandResult result = p4Rep.Connection.LastResults;
			if (result.ErrorList != null) // No errors returned
			{
				for (int i = 0; i < result.ErrorList.Count; i++)
				{
					log.Debug(result.ErrorList[i]);
				}
			}
			if (result.InfoOutput != null) // InfoOutput=.....Success:  Password verified./r/n3EAFE4C286C01E4C10AFC44269059DF4/r/n
			{
				log.Debug("result.InfoOutput=" + result.InfoOutput);
			}
			if (cred == null)
			{
				log.Debug("cred = null");
			}
			else
			{
				log.Debug("cred.Ticket=" + cred.Ticket); // cred.Ticket=Success:  Password verified.
				p4Rep.Connection.Credential = cred;
				try
				{
					// this line throws an error but the message says connection successful
					p4Rep.Connection.SetP4EnvironmentVar("P4PASSWD", cred.Ticket);
				}
				catch (Exception ex)
				{
					log.Debug(ex.Message); // ex.Message=registry: create key: The operation completed successfully.
				}
			}
			// Fails at this line with "Perforce password (P4PASSWD) invalid or unset."
			p4Rep.Connection.SetClient(workspace);
			log.Debug("workspace=" + p4Rep.Connection.Client.Name);
			log.Debug("Client.Root=" + p4Rep.Connection.Client.Root);
			log.Debug("Client.Description=" + p4Rep.Connection.Client.Description);
			p4Rep.Connection.Logout(null);
			p4Rep.Connection.Disconnect();
			Response.Write("Connected Successfully");
		}
		catch (Exception ex)
		{
			string err = ex.Message;
			Exception iex;
			if (ex.InnerException != null)
			{
				iex = ex.InnerException;
				while (iex != null)
				{
					err += "\r\n" + iex.Message;
					if (iex.InnerException != null)
					{
						iex = iex.InnerException;
					}
				}
			}
			log.Error("Error Connecting to Perforce\r\n" + err + "\r\n" + ex.StackTrace);
			Response.Write("Error Connecting to Perforce\r\n" + err + "\r\n" + ex.StackTrace);
		}

Do you have any code successfully logging in to Perforce on a Windows 2008 R2 Server that goes through the web (*note: the same or similar code works on a Windows 2003 Server)?

#16 enic21

enic21

    Newbie

  • Members
  • Pip
  • 9 posts

Posted 19 September 2013 - 10:19 PM

I was able to get around my original error (Fatal client error; disconnecting! open for write...) by setting the Identity of the Application pool to "LocalSystem".

In IIS click "Application Pools", right-click the application pool the site runs under, click "Advanced Settings...", and under Process Model change the "Identiy" to "LocalSystem" (Built-in account).

Thanks for all the help in resolving this.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users